ignore unpaired noteoff's when writing part of a MidiModel to a new source. in realit...
[ardour2.git] / libs / gtkmm2ext / cairocell.cc
blob5a7f4309ed5ac0118ec3d6d691131de0df0c1243
1 /*
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.
20 #include <algorithm>
21 #include <cmath>
22 #include <iostream>
24 #include "gtkmm2ext/cairocell.h"
25 #include "gtkmm2ext/utils.h"
27 using std::string;
28 using std::map;
29 using std::max;
30 using std::cerr;
31 using std::endl;
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;
43 break;
44 case Pango::STYLE_OBLIQUE:
45 _slant = Cairo::FONT_SLANT_OBLIQUE;
46 break;
47 case Pango::STYLE_ITALIC:
48 _slant = Cairo::FONT_SLANT_ITALIC;
49 break;
52 switch (fd.get_weight()) {
53 case Pango::WEIGHT_ULTRALIGHT:
54 _weight = Cairo::FONT_WEIGHT_NORMAL;
55 break;
57 case Pango::WEIGHT_LIGHT:
58 _weight = Cairo::FONT_WEIGHT_NORMAL;
59 break;
61 case Pango::WEIGHT_NORMAL:
62 _weight = Cairo::FONT_WEIGHT_NORMAL;
63 break;
65 case Pango::WEIGHT_SEMIBOLD:
66 _weight = Cairo::FONT_WEIGHT_BOLD;
67 break;
69 case Pango::WEIGHT_BOLD:
70 _weight = Cairo::FONT_WEIGHT_BOLD;
71 break;
73 case Pango::WEIGHT_ULTRABOLD:
74 _weight = Cairo::FONT_WEIGHT_BOLD;
75 break;
77 case Pango::WEIGHT_HEAVY:
78 _weight = Cairo::FONT_WEIGHT_BOLD;
79 break;
83 face = fd.get_family();
86 CairoCell::CairoCell (int32_t id)
87 : _id (id)
88 , _visible (true)
89 , _xpad (0)
91 bbox.x = 0;
92 bbox.y = 0;
93 bbox.width = 0;
94 bbox.height = 0;
97 CairoTextCell::CairoTextCell (int32_t id, double wc, boost::shared_ptr<CairoFontDescription> font)
98 : CairoCell (id)
99 , _width_chars (wc)
100 , _font (font)
101 , y_offset (0)
102 , x_offset (0)
106 void
107 CairoTextCell::set_text (const std::string& txt)
109 _text = txt;
112 void
113 CairoTextCell::render (Cairo::RefPtr<Cairo::Context>& context)
115 if (!_visible || _width_chars == 0) {
116 return;
119 context->save ();
121 context->rectangle (bbox.x, bbox.y, bbox.width, bbox.height);
122 context->clip ();
124 _font->apply (context);
125 context->move_to (bbox.x, bbox.y + bbox.height + y_offset);
126 context->show_text (_text);
128 context->restore ();
131 void
132 CairoTextCell::set_size (Cairo::RefPtr<Cairo::Context>& context)
134 const uint32_t lim = (uint32_t) ceil (_width_chars);
135 char buf[lim+1];
136 uint32_t n;
137 double max_width = 0.0;
138 double max_height = 0.0;
139 Cairo::TextExtents ext;
140 double bsum = 0;
142 buf[lim] = '\0';
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
164 by adding 2
166 bbox.height = max_height;
169 CairoCharCell::CairoCharCell (int32_t id, char c)
170 : CairoTextCell (id, 1)
172 _text = c;
175 void
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)
199 : editing_cell (0)
200 , _draw_bg (true)
201 , max_cell_width (0)
202 , max_cell_height (0)
203 , _corner_radius (9)
204 , _xpad (0)
205 , _ypad (0)
207 set_font (font);
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 */
222 bool
223 CairoEditableText::on_scroll_event (GdkEventScroll* ev)
225 CairoCell* cell = find_cell (ev->x, ev->y);
227 if (cell) {
228 return scroll (ev, cell);
231 return false;
234 bool
235 CairoEditableText::on_focus_in_event (GdkEventFocus* ev)
237 return false;
240 bool
241 CairoEditableText::on_focus_out_event (GdkEventFocus* ev)
243 if (editing_cell) {
244 queue_draw_cell (editing_cell);
245 editing_cell = 0;
247 return false;
250 void
251 CairoEditableText::add_cell (CairoCell* cell)
253 cells.push_back (cell);
255 CairoTextCell* tc = dynamic_cast<CairoTextCell*>(cell);
257 if (tc) {
258 tc->set_font (_font);
261 queue_resize ();
264 void
265 CairoEditableText::clear_cells ()
267 cells.clear ();
268 queue_resize ();
271 void
272 CairoEditableText::set_width_chars (CairoTextCell* cell, uint32_t wc)
274 if (cell) {
275 cell->set_width_chars (wc);
276 queue_resize ();
280 void
281 CairoEditableText::set_text (CairoTextCell* cell, const string& text)
283 cell->set_text (text);
284 queue_draw_cell (cell);
287 bool
288 CairoEditableText::on_expose_event (GdkEventExpose* ev)
290 Glib::RefPtr<Gdk::Window> win = get_window ();
292 if (!win) {
293 std::cerr << "CET: no window to draw on\n";
294 return false;
297 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
299 if (cells.empty()) {
300 return true;
303 context->rectangle (ev->area.x, ev->area.y, ev->area.width, ev->area.height);
304 context->clip ();
306 Gtk::Allocation alloc = get_allocation ();
307 double width = alloc.get_width();
308 double height = alloc.get_height ();
310 if (_draw_bg) {
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);
314 } else {
315 context->rectangle (0, 0, width, height);
317 context->fill ();
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);
330 } else {
331 context->set_source_rgba (r, g, b, a);
334 cell->render (context);
338 return true;
341 void
342 CairoEditableText::queue_draw_cell (CairoCell* cell)
344 Glib::RefPtr<Gdk::Window> win = get_window();
346 if (!win) {
347 return;
350 Gdk::Rectangle r;
352 r.set_x (cell->x());
353 r.set_y (cell->y());
354 r.set_width (cell->width());
355 r.set_height (cell->height());
357 Gdk::Region rg (r);
358 win->invalidate_region (rg, true);
361 CairoCell*
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)) {
366 return (*i);
370 return 0;
373 bool
374 CairoEditableText::on_button_press_event (GdkEventButton* ev)
376 CairoCell* cell = find_cell (ev->x, ev->y);
377 return button_press (ev, cell);
380 bool
381 CairoEditableText::on_button_release_event (GdkEventButton* ev)
383 CairoCell* cell = find_cell (ev->x, ev->y);
384 return button_release (ev, cell);
387 void
388 CairoEditableText::start_editing (CairoCell* cell)
390 stop_editing ();
392 if (cell) {
393 editing_cell = cell;
394 queue_draw_cell (cell);
395 grab_focus ();
399 void
400 CairoEditableText::stop_editing ()
402 if (editing_cell) {
403 queue_draw_cell (editing_cell);
404 editing_cell = 0;
408 void
409 CairoEditableText::set_cell_sizes ()
411 Glib::RefPtr<Gdk::Window> win = get_window();
413 if (!win) {
414 return;
417 Cairo::RefPtr<Cairo::Context> context = win->create_cairo_context();
419 if (!context) {
420 return;
423 for (CellMap::iterator i = cells.begin(); i != cells.end(); ++i) {
424 (*i)->set_size (context);
428 void
429 CairoEditableText::on_size_request (GtkRequisition* req)
431 set_cell_sizes ();
433 max_cell_width = 0;
434 max_cell_height = 0;
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;
445 void
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);
462 x += cell->width ();
464 if (++i != cells.end()) {
465 /* only add cell padding intra-cellularly */
466 x += cell->xpad();
467 } else {
468 break;
473 void
474 CairoEditableText::set_font (Pango::FontDescription& fd)
476 boost::shared_ptr<CairoFontDescription> cd (new CairoFontDescription (fd));
477 set_font (cd);
480 void
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)) {
486 tc->set_font (fd);
490 _font = fd;
492 queue_resize ();
493 queue_draw ();