Update Turkish translation
[dasher.git] / Src / Gtk2 / Canvas.cpp
blob322d77ca9a3439d281d3719b2a9bad847f062b2e
1 #include "../Common/Common.h"
3 #include "Canvas.h"
4 #include "DasherControl.h"
6 #include "../DasherCore/DasherTypes.h"
9 using namespace Dasher;
11 CCanvas::CCanvas(GtkWidget *pCanvas)
12 : CLabelListScreen(0,0) {
14 #if WITH_CAIRO
15 cairo_colours = 0;
16 #else
17 colours = 0;
18 #endif
20 m_pCanvas = pCanvas;
22 gtk_widget_add_events(m_pCanvas, GDK_ALL_EVENTS_MASK);
24 InitSurfaces(); //will create 0*0 surfaces. Is that a good idea? It seems to
25 // let us create PangoLayouts ok, which is what we need - but if we can
26 // create them without a backing surface at all, that might be better...
29 void CCanvas::InitSurfaces() {
30 // Construct the buffer pixmaps
31 // FIXME - only allocate without cairo
33 #if WITH_CAIRO
35 m_pDisplaySurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, GetWidth(), GetHeight());
36 m_pDecorationSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, GetWidth(), GetHeight());
37 //m_pOnscreenSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, GetWidth(), GetHeight());
39 #else
41 //m_pDummyBuffer = gdk_pixmap_new(m_pCanvas->window, GetWidth(), GetHeight(), -1);
43 m_pDisplayBuffer = gdk_pixmap_new(m_pCanvas->window, GetWidth(), GetHeight(), -1);
44 m_pDecorationBuffer = gdk_pixmap_new(m_pCanvas->window, GetWidth(), GetHeight(), -1);
45 //m_pOnscreenBuffer = gdk_pixmap_new(m_pCanvas->window, GetWidth(), GetHeight(), -1);
47 // Set the display buffer to be current
49 m_pOffscreenBuffer = m_pDisplayBuffer;
51 #endif
54 #if WITH_CAIRO
55 // The lines between origin and pointer is draw here
56 decoration_cr = cairo_create(m_pDecorationSurface);
57 cr = decoration_cr;
58 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
59 cairo_set_line_width(cr, 1.0);
61 // Base stuff are drawn here
62 display_cr = cairo_create(m_pDisplaySurface);
63 cr = display_cr;
64 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
65 cairo_set_line_width(cr, 1.0);
67 //onscreen_cr = cairo_create(m_pOnscreenSurface);
69 #endif
72 void CCanvas::DestroySurfaces() {
73 // Free the buffer pixmaps
75 #if WITH_CAIRO
76 cr = NULL;
77 cairo_surface_destroy(m_pDisplaySurface);
78 cairo_surface_destroy(m_pDecorationSurface);
79 //cairo_surface_destroy(m_pOnscreenSurface);
80 cairo_destroy(display_cr);
81 cairo_destroy(decoration_cr);
82 //cairo_destroy(onscreen_cr);
83 #else
84 //g_object_unref(m_pDummyBuffer);
85 g_object_unref(m_pDisplayBuffer);
86 g_object_unref(m_pDecorationBuffer);
87 //g_object_unref(m_pOnscreenBuffer);
88 #endif
91 CCanvas::~CCanvas() {
92 DestroySurfaces();
93 #if WITH_CAIRO
94 delete[] cairo_colours;
95 #endif
98 void CCanvas::resize(screenint w,screenint h) {
99 DestroySurfaces();
100 CDasherScreen::resize(w,h);
101 InitSurfaces();
104 bool CCanvas::IsWindowUnderCursor() {
105 GdkDisplay * gdkDisplay = gdk_display_get_default();
106 gint winx,winy;
107 // unfortunately under X we cannot arbritrarily find which window
108 // lives at a location. Only underneath the cursor.
109 GdkWindow * window = gdk_display_get_window_at_pointer(gdkDisplay, &winx, &winy);
110 return window == gtk_widget_get_window(m_pCanvas);
114 void CCanvas::Display() {
115 // FIXME - Some of this stuff is probably not needed
116 // GdkRectangle update_rect;
118 #if WITH_CAIRO
119 #else
120 GdkGC *graphics_context;
121 GdkColormap *colormap;
123 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
124 colormap = gdk_colormap_get_system();
125 #endif
127 BEGIN_DRAWING;
128 SET_COLOR(0);
130 // Copy the offscreen buffer into the onscreen buffer
132 // TODO: Reimplement (kind of important!)
134 // gdk_draw_drawable(m_pOnscreenBuffer, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pOffscreenBuffer, 0, 0, 0, 0, GetWidth(), GetHeight());
136 // BEGIN_DRAWING;
138 // cairo_set_source_surface(onscreen_cr, m_pDecorationSurface, 0, 0);
139 // cairo_rectangle(onscreen_cr, 0, 0, GetWidth(), GetHeight());
140 // cairo_fill(onscreen_cr);
144 // END_DRAWING;
146 // Blank the offscreen buffer (?)
148 // gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, TRUE, 0, 0, GetWidth(), GetHeight());
150 // Invalidate the full canvas to force it to be redrawn on-screen
152 // update_rect.x = 0;
153 // update_rect.y = 0;
154 // update_rect.width = GetWidth();
155 // update_rect.height = GetHeight();
157 // gdk_window_invalidate_rect(m_pCanvas->window, &update_rect, FALSE);
159 // BEGIN_DRAWING;
161 // GdkRectangle sRect = {0, 0, GetWidth(), GetHeight()};
162 // gdk_window_begin_paint_rect(m_pCanvas->window, &sRect);
164 #if WITH_CAIRO
165 cairo_t *widget_cr;
166 widget_cr = gdk_cairo_create(gtk_widget_get_window(m_pCanvas));
167 cairo_set_source_surface(widget_cr, m_pDecorationSurface, 0, 0);
168 cairo_rectangle(widget_cr, 0, 0, GetWidth(), GetHeight());
169 cairo_fill(widget_cr);
170 cairo_destroy(widget_cr);
171 #else
172 gdk_draw_drawable(m_pCanvas->window, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pDecorationBuffer, 0, 0, 0, 0, GetWidth(), GetHeight());
173 #endif
175 // gdk_window_end_paint(m_pCanvas->window);
177 // Restore original graphics context (colours)
179 END_DRAWING;
181 // gtk_main_iteration_do(0);
184 void CCanvas::DrawRectangle(screenint x1, screenint y1, screenint x2, screenint y2, int Color, int iOutlineColour, int iThickness) {
186 // std::cout << "Raw Rectangle, (" << x1 << ", " << y1 << ") - (" << x2 << ", " << y2 << ")" << std::endl;
188 #if WITH_CAIRO
189 #else
190 GdkGC *graphics_context;
191 GdkColormap *colormap;
193 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
194 colormap = gdk_colormap_get_system();
195 #endif
197 BEGIN_DRAWING;
199 int iLeft;
200 int iTop;
201 int iWidth;
202 int iHeight;
204 if( x2 > x1 ) {
205 iLeft = x1;
206 iWidth = x2 - x1;
208 else {
209 iLeft = x2;
210 iWidth = x1 - x2;
213 if( y2 > y1 ) {
214 iTop = y1;
215 iHeight = y2 - y1;
217 else {
218 iTop = y2;
219 iHeight = y1 - y2;
222 // std::cout << bFill << " " << Color << " " << iLeft << " " << iTop << " " << iWidth << " " << iHeight << std::endl;
224 if(Color!=-1) {
225 SET_COLOR(Color);
226 #if WITH_CAIRO
227 //cairo_set_line_width(cr, iThickness); //outline done below
228 cairo_rectangle(cr, iLeft, iTop, iWidth, iHeight);
229 cairo_fill(cr);
230 #else
231 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, TRUE, iLeft, iTop, iWidth + 1, iHeight + 1);
232 #endif
235 if(iThickness>0) {
236 if( iOutlineColour == -1 )
237 SET_COLOR(3);
238 else
239 SET_COLOR(iOutlineColour);
241 // TODO: There's a known issue here when the start of one box and
242 // the end of the other map to the same pixel. This generally
243 // results in box outlines being truncated.
245 #if WITH_CAIRO
246 cairo_set_line_width(cr, iThickness);
247 cairo_rectangle(cr, iLeft + 0.5, iTop + 0.5, iWidth, iHeight);
248 cairo_stroke(cr);
249 #else
250 gdk_gc_set_line_attributes(graphics_context, iThickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
251 gdk_draw_rectangle(m_pOffscreenBuffer, graphics_context, FALSE, iLeft, iTop, iWidth, iHeight);
252 #endif
254 END_DRAWING;
257 void CCanvas::DrawCircle(screenint iCX, screenint iCY, screenint iR, int iFillColour, int iLineColour, int iThickness) {
258 #if WITH_CAIRO
259 #else
260 GdkGC *graphics_context;
261 GdkColormap *colormap;
263 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
264 colormap = gdk_colormap_get_system();
265 #endif
267 BEGIN_DRAWING;
269 if(iFillColour!=-1) {
270 SET_COLOR(iFillColour);
271 #if WITH_CAIRO
272 cairo_arc(cr, iCX, iCY, iR, 0, 2*M_PI);
273 cairo_fill(cr);
274 #else
275 gdk_draw_arc(m_pOffscreenBuffer, graphics_context, true, iCX - iR, iCY - iR, 2*iR, 2*iR, 0, 23040);
276 #endif
279 if (iThickness>0) {
280 SET_COLOR(iLineColour);
281 #if WITH_CAIRO
282 cairo_set_line_width(cr, iThickness);
283 cairo_arc(cr, iCX, iCY, iR, 0, 2*M_PI);
284 cairo_stroke(cr);
285 #else
286 //note fiddle on iThickness, allegedly "to make it work on Windows"(?)...
287 gdk_gc_set_line_attributes(graphics_context, iThickness==1 ? 0 : iThickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
288 gdk_draw_arc(m_pOffscreenBuffer, graphics_context, false, iCX - iR, iCY - iR, 2*iR, 2*iR, 0, 23040);
289 #endif
292 END_DRAWING;
295 void CCanvas::Polygon(Dasher::CDasherScreen::point *Points, int Number, int fillColour, int outlineColour, int iWidth) {
297 //(ACL) commenting out, we now deal with fill & outline separately. However,
298 // TODO: find a windows box on which this actually applies and test it
299 //if(iWidth == 1) // This is to make it work properly on Windows
300 // iWidth = 0;
302 #if WITH_CAIRO
303 #else
304 GdkGC *graphics_context;
305 GdkColormap *colormap;
307 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
308 colormap = gdk_colormap_get_system();
309 #endif
311 BEGIN_DRAWING;
313 #if WITH_CAIRO
314 cairo_move_to(cr, Points[0].x, Points[0].y);
315 for (int i=1; i < Number; i++)
316 cairo_line_to(cr, Points[i].x, Points[i].y);
317 cairo_close_path(cr);
318 if (fillColour!=-1) {
319 SET_COLOR(fillColour);
320 if (iWidth<=0) {
321 cairo_fill(cr); //fill only, no outline
322 } else {
323 cairo_fill_preserve(cr); //leave path defined for cairo_stroke, below
326 if (iWidth>0) {
327 SET_COLOR(outlineColour==-1 ? 3 : outlineColour);
328 cairo_stroke(cr);
330 #else
331 GdkPoint *gdk_points;
332 gdk_points = (GdkPoint *) g_malloc(Number * sizeof(GdkPoint));
334 for(int i = 0; i < Number; i++) {
335 gdk_points[i].x = Points[i].x;
336 gdk_points[i].y = Points[i].y;
339 if (fillColour != -1) {
340 SET_COLOR(fillColour);
341 gdk_gc_set_line_attributes(graphics_context, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
342 gdk_draw_polygon(m_pOffscreenBuffer, graphics_context, TRUE, gdk_points, Number);
344 if (iWidth > 0) {
345 SET_COLOR(outlineColour);
346 gdk_gc_set_line_attributes(graphics_context, iWidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
347 gdk_draw_polygon(m_pOffscreenBuffer, graphics_context, FALSE, gdk_points, Number);
349 g_free(gdk_points);
350 #endif
352 END_DRAWING;
355 void CCanvas::Polyline(Dasher::CDasherScreen::point *Points, int Number, int iWidth, int Colour) {
357 // FIXME - combine this with polygon?
359 #if WITH_CAIRO
360 #else
361 if(iWidth == 1) // This is to make it work propely on Windows
362 iWidth = 0;
364 GdkGC *graphics_context;
365 GdkColormap *colormap;
367 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
368 colormap = gdk_colormap_get_system();
369 #endif
371 BEGIN_DRAWING;
372 SET_COLOR(Colour);
374 #if WITH_CAIRO
375 cairo_set_line_width(cr, iWidth);
376 cairo_move_to(cr, Points[0].x+.5, Points[0].y+.5);
377 for (int i=1; i < Number; i++)
378 cairo_line_to(cr, Points[i].x+.5, Points[i].y+.5);
379 cairo_stroke(cr);
380 #else
381 GdkPoint *gdk_points;
382 gdk_points = (GdkPoint *) g_malloc(Number * sizeof(GdkPoint));
384 gdk_gc_set_line_attributes(graphics_context, iWidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
386 for(int i = 0; i < Number; i++) {
387 gdk_points[i].x = Points[i].x;
388 gdk_points[i].y = Points[i].y;
391 gdk_draw_lines(m_pOffscreenBuffer, graphics_context, gdk_points, Number);
392 g_free(gdk_points);
393 #endif
395 END_DRAWING;
398 CDasherScreen::Label *CCanvas::MakeLabel(const string &strText, unsigned int iWrapFontSize) {
399 return new CPangoLabel(this, strText, iWrapFontSize);
402 void CCanvas::SetFont(const std::string &strName) {
403 m_strFontName=strName;
404 for (map<unsigned int,PangoFontDescription *>::iterator it=m_mFonts.begin(); it!=m_mFonts.end(); it++) {
405 pango_font_description_free(it->second);
406 it->second = pango_font_description_from_string(m_strFontName.c_str());
407 pango_font_description_set_size(it->second,it->first * PANGO_SCALE);
409 for (set<CLabelListScreen::Label *>::iterator it=LabelsBegin(); it!=LabelsEnd(); it++) {
410 map<unsigned int,PangoLayout *> &layouts(static_cast<CPangoLabel *>(*it)->m_mLayouts);
411 for (map<unsigned int,PangoLayout *>::iterator it2=layouts.begin(); it2!=layouts.end(); it2++) {
412 DASHER_ASSERT(m_mFonts.find(it2->first) != m_mFonts.end()); //central font repository knows about this size
413 pango_layout_set_font_description(it2->second,m_mFonts[it2->first]);
418 PangoLayout *CCanvas::GetLayout(CPangoLabel *label, unsigned int iFontSize) {
420 map<unsigned int,PangoLayout *>::iterator it = label->m_mLayouts.find(iFontSize);
421 if (it != label->m_mLayouts.end()) return it->second;
423 #if WITH_CAIRO
424 PangoLayout *pNewPangoLayout(pango_cairo_create_layout(cr));
425 #else
426 PangoLayout *pNewPangoLayout(gtk_widget_create_pango_layout(m_pCanvas, ""));
427 #endif
428 label->m_mLayouts.insert(pair<unsigned int,PangoLayout *>(iFontSize, pNewPangoLayout));
429 if (label->m_iWrapSize) pango_layout_set_width(pNewPangoLayout, GetWidth() * PANGO_SCALE);
430 pango_layout_set_text(pNewPangoLayout, label->m_strText.c_str(), -1);
432 PangoFontDescription *pF;
434 map<unsigned int,PangoFontDescription *>::iterator it = m_mFonts.find(iFontSize);
435 if (it != m_mFonts.end())
436 pF=it->second;
437 else {
438 pF = pango_font_description_from_string(m_strFontName.c_str());
439 pango_font_description_set_size(pF, iFontSize * PANGO_SCALE);
440 m_mFonts[iFontSize] = pF;
442 pango_layout_set_font_description(pNewPangoLayout, pF);
444 return pNewPangoLayout;
447 void CCanvas::DrawString(CDasherScreen::Label *label, screenint x1, screenint y1, unsigned int size, int iColor) {
449 #if WITH_CAIRO
450 #else
451 GdkGC *graphics_context;
452 GdkColormap *colormap;
454 graphics_context = m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)];
455 colormap = gdk_colormap_get_system();
456 #endif
458 BEGIN_DRAWING;
459 SET_COLOR(iColor);
461 PangoLayout *pLayout(GetLayout(static_cast<CPangoLabel*>(label),size));
463 PangoRectangle sPangoInk;
465 pango_layout_get_pixel_extents(pLayout, &sPangoInk, NULL);
466 x1 -= sPangoInk.x;
467 y1 -= sPangoInk.y;
468 #if WITH_CAIRO
469 cairo_translate(cr, x1, y1);
470 pango_cairo_show_layout(cr, pLayout);
471 #else
472 gdk_draw_layout(m_pOffscreenBuffer, graphics_context, x1, y1, pLayout);
473 #endif
475 END_DRAWING;
478 pair<screenint,screenint> CCanvas::TextSize(CDasherScreen::Label *label, unsigned int size) {
479 PangoLayout *pLayout(GetLayout(static_cast<CPangoLabel*>(label),size));
481 PangoRectangle sPangoInk;
482 pango_layout_get_pixel_extents(pLayout, &sPangoInk, NULL);
484 return pair<screenint,screenint>(sPangoInk.width,sPangoInk.height);
487 void CCanvas::SendMarker(int iMarker) {
489 switch(iMarker) {
490 case 0: // Switch to display buffer
491 #if WITH_CAIRO
492 cr = display_cr;
493 #else
494 m_pOffscreenBuffer = m_pDisplayBuffer;
495 #endif
496 break;
497 case 1: // Switch to decorations buffer
499 #if WITH_CAIRO
500 cairo_set_source_surface(decoration_cr, m_pDisplaySurface, 0, 0);
501 cairo_rectangle(decoration_cr, 0, 0, GetWidth(), GetHeight());
502 cairo_fill(decoration_cr);
503 cr = decoration_cr;
504 #else
505 gdk_draw_drawable(m_pDecorationBuffer, m_pCanvas->style->fg_gc[GTK_WIDGET_STATE(m_pCanvas)], m_pDisplayBuffer, 0, 0, 0, 0, GetWidth(), GetHeight());
506 m_pOffscreenBuffer = m_pDecorationBuffer;
507 #endif
508 break;
512 void CCanvas::SetColourScheme(const CColourIO::ColourInfo *pColourScheme) {
513 int iNumColours(pColourScheme->Reds.size());
515 #if WITH_CAIRO
516 if (cairo_colours)
517 delete[] cairo_colours;
518 cairo_colours = new cairo_pattern_t*[iNumColours];
519 #else
520 if (colours)
521 delete[] colours;
522 colours = new GdkColor[iNumColours];
523 #endif
525 for(int i = 0; i < iNumColours; i++) {
526 #if WITH_CAIRO
527 cairo_colours[i] = cairo_pattern_create_rgb (
528 pColourScheme->Reds[i] / 255.0,
529 pColourScheme->Greens[i] / 255.0,
530 pColourScheme->Blues[i] / 255.0
532 #else
533 colours[i].pixel=0;
534 colours[i].red=pColourScheme->Reds[i]*257;
535 colours[i].green=pColourScheme->Greens[i]*257;
536 colours[i].blue=pColourScheme->Blues[i]*257;
537 #endif
541 bool CCanvas::GetCanvasSize(GdkRectangle *pRectangle)
543 if ((pRectangle == NULL) || (m_pCanvas == NULL))
544 return false;
546 // Using gtk_window_get_frame_extents() only seems to return the position
547 // and size of the parent Dasher window. So we'll get the widgets position
548 // and use its size to determine the bounding rectangle.
549 int iX = 0;
550 int iY = 0;
552 gdk_window_get_position(gtk_widget_get_window(m_pCanvas), &iX, &iY);
554 pRectangle->x = iX;
555 pRectangle->y = iY;
556 pRectangle->width = GetWidth();
557 pRectangle->height = GetHeight();
559 return true;