1 #include "../Common/Common.h"
4 #include "DasherControl.h"
6 #include "../DasherCore/DasherTypes.h"
9 using namespace Dasher
;
11 CCanvas::CCanvas(GtkWidget
*pCanvas
)
12 : CLabelListScreen(0,0) {
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
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());
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
;
55 // The lines between origin and pointer is draw here
56 decoration_cr
= cairo_create(m_pDecorationSurface
);
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
);
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);
72 void CCanvas::DestroySurfaces() {
73 // Free the buffer pixmaps
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);
84 //g_object_unref(m_pDummyBuffer);
85 g_object_unref(m_pDisplayBuffer
);
86 g_object_unref(m_pDecorationBuffer
);
87 //g_object_unref(m_pOnscreenBuffer);
94 delete[] cairo_colours
;
98 void CCanvas::resize(screenint w
,screenint h
) {
100 CDasherScreen::resize(w
,h
);
104 bool CCanvas::IsWindowUnderCursor() {
105 GdkDisplay
* gdkDisplay
= gdk_display_get_default();
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;
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();
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());
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);
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);
161 // GdkRectangle sRect = {0, 0, GetWidth(), GetHeight()};
162 // gdk_window_begin_paint_rect(m_pCanvas->window, &sRect);
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
);
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());
175 // gdk_window_end_paint(m_pCanvas->window);
177 // Restore original graphics context (colours)
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;
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();
222 // std::cout << bFill << " " << Color << " " << iLeft << " " << iTop << " " << iWidth << " " << iHeight << std::endl;
227 //cairo_set_line_width(cr, iThickness); //outline done below
228 cairo_rectangle(cr
, iLeft
, iTop
, iWidth
, iHeight
);
231 gdk_draw_rectangle(m_pOffscreenBuffer
, graphics_context
, TRUE
, iLeft
, iTop
, iWidth
+ 1, iHeight
+ 1);
236 if( iOutlineColour
== -1 )
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.
246 cairo_set_line_width(cr
, iThickness
);
247 cairo_rectangle(cr
, iLeft
+ 0.5, iTop
+ 0.5, iWidth
, iHeight
);
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
);
257 void CCanvas::DrawCircle(screenint iCX
, screenint iCY
, screenint iR
, int iFillColour
, int iLineColour
, int iThickness
) {
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();
269 if(iFillColour
!=-1) {
270 SET_COLOR(iFillColour
);
272 cairo_arc(cr
, iCX
, iCY
, iR
, 0, 2*M_PI
);
275 gdk_draw_arc(m_pOffscreenBuffer
, graphics_context
, true, iCX
- iR
, iCY
- iR
, 2*iR
, 2*iR
, 0, 23040);
280 SET_COLOR(iLineColour
);
282 cairo_set_line_width(cr
, iThickness
);
283 cairo_arc(cr
, iCX
, iCY
, iR
, 0, 2*M_PI
);
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);
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
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();
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
);
321 cairo_fill(cr
); //fill only, no outline
323 cairo_fill_preserve(cr
); //leave path defined for cairo_stroke, below
327 SET_COLOR(outlineColour
==-1 ? 3 : outlineColour
);
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
);
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
);
355 void CCanvas::Polyline(Dasher::CDasherScreen::point
*Points
, int Number
, int iWidth
, int Colour
) {
357 // FIXME - combine this with polygon?
361 if(iWidth
== 1) // This is to make it work propely on Windows
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();
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);
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
);
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
;
424 PangoLayout
*pNewPangoLayout(pango_cairo_create_layout(cr
));
426 PangoLayout
*pNewPangoLayout(gtk_widget_create_pango_layout(m_pCanvas
, ""));
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())
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
) {
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();
461 PangoLayout
*pLayout(GetLayout(static_cast<CPangoLabel
*>(label
),size
));
463 PangoRectangle sPangoInk
;
465 pango_layout_get_pixel_extents(pLayout
, &sPangoInk
, NULL
);
469 cairo_translate(cr
, x1
, y1
);
470 pango_cairo_show_layout(cr
, pLayout
);
472 gdk_draw_layout(m_pOffscreenBuffer
, graphics_context
, x1
, y1
, pLayout
);
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
) {
490 case 0: // Switch to display buffer
494 m_pOffscreenBuffer
= m_pDisplayBuffer
;
497 case 1: // Switch to decorations buffer
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
);
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
;
512 void CCanvas::SetColourScheme(const CColourIO::ColourInfo
*pColourScheme
) {
513 int iNumColours(pColourScheme
->Reds
.size());
517 delete[] cairo_colours
;
518 cairo_colours
= new cairo_pattern_t
*[iNumColours
];
522 colours
= new GdkColor
[iNumColours
];
525 for(int i
= 0; i
< iNumColours
; i
++) {
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
534 colours
[i
].red
=pColourScheme
->Reds
[i
]*257;
535 colours
[i
].green
=pColourScheme
->Greens
[i
]*257;
536 colours
[i
].blue
=pColourScheme
->Blues
[i
]*257;
541 bool CCanvas::GetCanvasSize(GdkRectangle
*pRectangle
)
543 if ((pRectangle
== NULL
) || (m_pCanvas
== NULL
))
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.
552 gdk_window_get_position(gtk_widget_get_window(m_pCanvas
), &iX
, &iY
);
556 pRectangle
->width
= GetWidth();
557 pRectangle
->height
= GetHeight();