2 Copyright (C) 2011 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "gtkmm2ext/cairocell.h"
25 #include "gtkmm2ext/utils.h"
32 using namespace Gtkmm2ext
;
34 static const double cairo_font_fudge
= 1.5;
36 CairoFontDescription::CairoFontDescription (Pango::FontDescription
& fd
)
38 _size
= cairo_font_fudge
* (fd
.get_size() / PANGO_SCALE
);
40 switch (fd
.get_style()) {
41 case Pango::STYLE_NORMAL
:
42 _slant
= Cairo::FONT_SLANT_NORMAL
;
44 case Pango::STYLE_OBLIQUE
:
45 _slant
= Cairo::FONT_SLANT_OBLIQUE
;
47 case Pango::STYLE_ITALIC
:
48 _slant
= Cairo::FONT_SLANT_ITALIC
;
52 switch (fd
.get_weight()) {
53 case Pango::WEIGHT_ULTRALIGHT
:
54 _weight
= Cairo::FONT_WEIGHT_NORMAL
;
57 case Pango::WEIGHT_LIGHT
:
58 _weight
= Cairo::FONT_WEIGHT_NORMAL
;
61 case Pango::WEIGHT_NORMAL
:
62 _weight
= Cairo::FONT_WEIGHT_NORMAL
;
65 case Pango::WEIGHT_SEMIBOLD
:
66 _weight
= Cairo::FONT_WEIGHT_BOLD
;
69 case Pango::WEIGHT_BOLD
:
70 _weight
= Cairo::FONT_WEIGHT_BOLD
;
73 case Pango::WEIGHT_ULTRABOLD
:
74 _weight
= Cairo::FONT_WEIGHT_BOLD
;
77 case Pango::WEIGHT_HEAVY
:
78 _weight
= Cairo::FONT_WEIGHT_BOLD
;
83 face
= fd
.get_family();
86 CairoCell::CairoCell (int32_t id
)
97 CairoTextCell::CairoTextCell (int32_t id
, double wc
, boost::shared_ptr
<CairoFontDescription
> font
)
107 CairoTextCell::set_text (const std::string
& txt
)
113 CairoTextCell::render (Cairo::RefPtr
<Cairo::Context
>& context
)
115 if (!_visible
|| _width_chars
== 0) {
121 context
->rectangle (bbox
.x
, bbox
.y
, bbox
.width
, bbox
.height
);
124 _font
->apply (context
);
125 context
->move_to (bbox
.x
, bbox
.y
+ bbox
.height
+ y_offset
);
126 context
->show_text (_text
);
132 CairoTextCell::set_size (Cairo::RefPtr
<Cairo::Context
>& context
)
134 const uint32_t lim
= (uint32_t) ceil (_width_chars
);
137 double max_width
= 0.0;
138 double max_height
= 0.0;
139 Cairo::TextExtents ext
;
144 _font
->apply (context
);
146 for (int digit
= 0; digit
< 10; digit
++) {
148 for (n
= 0; n
< lim
; ++n
) {
149 buf
[n
] = '0' + digit
;
152 context
->get_text_extents (buf
, ext
);
154 max_width
= max (ext
.width
+ ext
.x_bearing
, max_width
);
155 max_height
= max (ext
.height
, max_height
);
156 bsum
+= ext
.x_bearing
;
159 /* add the average x-bearing for all digits as right hand side padding */
161 bbox
.width
= max_width
+ (bsum
/10.0);
163 /* some fonts and some digits get their extents computed "too small", so fudge this
166 bbox
.height
= max_height
;
169 CairoCharCell::CairoCharCell (int32_t id
, char c
)
170 : CairoTextCell (id
, 1)
176 CairoCharCell::set_size (Cairo::RefPtr
<Cairo::Context
>& context
)
178 Cairo::TextExtents ext
;
180 _font
->apply (context
);
183 const char* buf
= "8";
184 context
->get_text_extents (buf
, ext
);
185 /* same height as an "8" */
186 bbox
.height
= ext
.height
;
190 const char* buf
= ":";
191 context
->get_text_extents (buf
, ext
);
192 bbox
.width
= ext
.width
+ (2.0 * ext
.x_bearing
);
193 /* center vertically */
194 y_offset
= (ext
.height
- bbox
.height
) / 2.0;
198 CairoEditableText::CairoEditableText (boost::shared_ptr
<CairoFontDescription
> font
)
202 , max_cell_height (0)
209 add_events (Gdk::POINTER_MOTION_HINT_MASK
| Gdk::SCROLL_MASK
| Gdk::KEY_PRESS_MASK
| Gdk::KEY_RELEASE_MASK
|
210 Gdk::BUTTON_PRESS_MASK
| Gdk::BUTTON_RELEASE_MASK
| Gdk::SCROLL_MASK
);
211 set_flags (Gtk::CAN_FOCUS
);
213 set_can_default (true);
214 set_receives_default (true);
217 CairoEditableText::~CairoEditableText ()
219 /* we don't own cells */
223 CairoEditableText::on_scroll_event (GdkEventScroll
* ev
)
225 CairoCell
* cell
= find_cell (ev
->x
, ev
->y
);
228 return scroll (ev
, cell
);
235 CairoEditableText::on_focus_in_event (GdkEventFocus
* ev
)
241 CairoEditableText::on_focus_out_event (GdkEventFocus
* ev
)
244 queue_draw_cell (editing_cell
);
251 CairoEditableText::add_cell (CairoCell
* cell
)
253 cells
.push_back (cell
);
255 CairoTextCell
* tc
= dynamic_cast<CairoTextCell
*>(cell
);
258 tc
->set_font (_font
);
265 CairoEditableText::clear_cells ()
272 CairoEditableText::set_width_chars (CairoTextCell
* cell
, uint32_t wc
)
275 cell
->set_width_chars (wc
);
281 CairoEditableText::set_text (CairoTextCell
* cell
, const string
& text
)
283 cell
->set_text (text
);
284 queue_draw_cell (cell
);
288 CairoEditableText::on_expose_event (GdkEventExpose
* ev
)
290 Glib::RefPtr
<Gdk::Window
> win
= get_window ();
293 std::cerr
<< "CET: no window to draw on\n";
297 Cairo::RefPtr
<Cairo::Context
> context
= win
->create_cairo_context();
303 context
->rectangle (ev
->area
.x
, ev
->area
.y
, ev
->area
.width
, ev
->area
.height
);
306 Gtk::Allocation alloc
= get_allocation ();
307 double width
= alloc
.get_width();
308 double height
= alloc
.get_height ();
311 context
->set_source_rgba (bg_r
, bg_g
, bg_b
, bg_a
);
312 if (_corner_radius
) {
313 rounded_rectangle (context
, 0, 0, width
, height
, _corner_radius
);
315 context
->rectangle (0, 0, width
, height
);
320 for (CellMap::iterator i
= cells
.begin(); i
!= cells
.end(); ++i
) {
322 CairoCell
* cell
= (*i
);
324 /* is cell inside the expose area?
327 if (cell
->intersects (ev
->area
)) {
328 if (cell
== editing_cell
) {
329 context
->set_source_rgba (edit_r
, edit_b
, edit_g
, edit_a
);
331 context
->set_source_rgba (r
, g
, b
, a
);
334 cell
->render (context
);
342 CairoEditableText::queue_draw_cell (CairoCell
* cell
)
344 Glib::RefPtr
<Gdk::Window
> win
= get_window();
354 r
.set_width (cell
->width());
355 r
.set_height (cell
->height());
358 win
->invalidate_region (rg
, true);
362 CairoEditableText::find_cell (uint32_t x
, uint32_t y
)
364 for (CellMap::iterator i
= cells
.begin(); i
!= cells
.end(); ++i
) {
365 if ((*i
)->covers (x
, y
)) {
374 CairoEditableText::on_button_press_event (GdkEventButton
* ev
)
376 CairoCell
* cell
= find_cell (ev
->x
, ev
->y
);
377 return button_press (ev
, cell
);
381 CairoEditableText::on_button_release_event (GdkEventButton
* ev
)
383 CairoCell
* cell
= find_cell (ev
->x
, ev
->y
);
384 return button_release (ev
, cell
);
388 CairoEditableText::start_editing (CairoCell
* cell
)
394 queue_draw_cell (cell
);
400 CairoEditableText::stop_editing ()
403 queue_draw_cell (editing_cell
);
409 CairoEditableText::set_cell_sizes ()
411 Glib::RefPtr
<Gdk::Window
> win
= get_window();
417 Cairo::RefPtr
<Cairo::Context
> context
= win
->create_cairo_context();
423 for (CellMap::iterator i
= cells
.begin(); i
!= cells
.end(); ++i
) {
424 (*i
)->set_size (context
);
429 CairoEditableText::on_size_request (GtkRequisition
* req
)
436 for (CellMap::iterator i
= cells
.begin(); i
!= cells
.end(); ++i
) {
437 max_cell_width
+= (*i
)->width();
438 max_cell_height
= std::max ((double) (*i
)->height(), max_cell_height
);
441 req
->width
= max_cell_width
;
442 req
->height
= max_cell_height
;
446 CairoEditableText::on_size_allocate (Gtk::Allocation
& alloc
)
448 Misc::on_size_allocate (alloc
);
450 /* position each cell so that its centered in the allocated space
453 double x
= (alloc
.get_width() - max_cell_width
)/2.0;
454 double y
= (alloc
.get_height() - max_cell_height
)/2.0;
456 CellMap::iterator i
= cells
.begin();
458 while (i
!= cells
.end()) {
459 CairoCell
* cell
= (*i
);
461 cell
->set_position (x
, y
);
464 if (++i
!= cells
.end()) {
465 /* only add cell padding intra-cellularly */
474 CairoEditableText::set_font (Pango::FontDescription
& fd
)
476 boost::shared_ptr
<CairoFontDescription
> cd (new CairoFontDescription (fd
));
481 CairoEditableText::set_font (boost::shared_ptr
<CairoFontDescription
> fd
)
483 for (CellMap::iterator i
= cells
.begin(); i
!= cells
.end(); ++i
) {
484 CairoTextCell
* tc
= dynamic_cast<CairoTextCell
*>(*i
);
485 if (tc
&& (!tc
->font() || tc
->font() == _font
)) {