changing the clamp_width of an ArdourCanvas::Text needs to potentially provoke an...
[ardour.git] / libs / canvas / text.cc
1 /*
2     Copyright (C) 2011-2013 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <gdk/gdk.h>
21
22 #include <cairomm/cairomm.h>
23 #include <gtkmm/label.h>
24
25 #include "pbd/stacktrace.h"
26
27 #include "canvas/text.h"
28 #include "canvas/canvas.h"
29 #include "canvas/utils.h"
30
31 using namespace std;
32 using namespace ArdourCanvas;
33
34 Text::Text (Canvas* c)
35         : Item (c)
36         , _color (0x000000ff)
37         , _font_description (0)
38         , _alignment (Pango::ALIGN_LEFT)
39         , _width (0)
40         , _height (0)
41         , _need_redraw (false)
42         , _clamped_width (COORD_MAX)
43 {
44         _outline = false;
45 }
46
47 Text::Text (Item* parent)
48         : Item (parent)
49         , _color (0x000000ff)
50         , _font_description (0)
51         , _alignment (Pango::ALIGN_LEFT)
52         , _width (0)
53         , _height (0)
54         , _need_redraw (false)
55         , _clamped_width (COORD_MAX)
56 {
57         _outline = false;
58 }
59
60 Text::~Text ()
61 {
62         delete _font_description;
63 }
64
65 void
66 Text::set (string const & text)
67 {
68         begin_change ();
69         
70         _text = text;
71
72         _need_redraw = true;
73         _bounding_box_dirty = true;
74
75         end_change ();
76 }
77
78 void
79 Text::redraw (Cairo::RefPtr<Cairo::Context> context) const
80 {
81         if (_text.empty()) {
82                 return;
83         }
84
85         Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
86
87         _redraw (layout);
88 }
89
90 void
91 Text::redraw (Glib::RefPtr<Pango::Context> context) const
92 {
93         if (_text.empty()) {
94                 return;
95         }
96
97         Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
98         _redraw (layout);
99 }
100
101 void
102 Text::_redraw (Glib::RefPtr<Pango::Layout> layout) const
103 {
104         layout->set_text (_text);
105
106         if (_font_description) {
107                 layout->set_font_description (*_font_description);
108         }
109
110         layout->set_alignment (_alignment);
111
112         int w;
113         int h;
114
115         layout->get_pixel_size (w, h);
116
117         _width = w;
118         _height = h;
119
120         _image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
121
122         Cairo::RefPtr<Cairo::Context> img_context = Cairo::Context::create (_image);
123
124         /* and draw, in the appropriate color of course */
125
126         if (_outline) {
127                 set_source_rgba (img_context, _outline_color);
128                 layout->update_from_cairo_context (img_context);
129                 pango_cairo_layout_path (img_context->cobj(), layout->gobj());
130                 img_context->stroke_preserve ();
131                 set_source_rgba (img_context, _color);
132                 img_context->fill ();
133         } else {
134                 set_source_rgba (img_context, _color);
135                 layout->show_in_cairo_context (img_context);
136         }
137
138         /* text has now been rendered in _image and is ready for blit in
139          * ::render 
140          */
141
142         _need_redraw = false;
143 }
144
145 void
146 Text::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
147 {
148         if (_text.empty()) {
149                 return;
150         }
151
152         Rect self = item_to_window (Rect (0, 0, min (_clamped_width, (double)_image->get_width ()), _image->get_height ()));
153         boost::optional<Rect> i = self.intersection (area);
154         
155         if (!i) {
156                 return;
157         }
158
159         if (_need_redraw) {
160                 redraw (context);
161         }
162         
163         Rect intersection (i.get());
164
165         context->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
166         context->set_source (_image, self.x0, self.y0);
167         context->fill ();
168 }
169
170 void
171 Text::clamp_width (double w)
172 {
173         begin_change ();
174         _clamped_width = w;
175         _bounding_box_dirty = true;
176         end_change ();
177 }
178
179 void
180 Text::compute_bounding_box () const
181 {
182         if (!_canvas || _text.empty()) {
183                 _bounding_box = boost::optional<Rect> ();
184                 _bounding_box_dirty = false;
185                 return;
186         }
187
188         if (_bounding_box_dirty) {
189                 if (_need_redraw || !_image) {
190                         Glib::RefPtr<Pango::Context> context = Glib::wrap (gdk_pango_context_get()); // context now owns C object and will free it
191                         redraw (context);
192                 }
193                 _bounding_box = Rect (0, 0, min (_clamped_width, (double) _image->get_width()), _image->get_height());
194                 _bounding_box_dirty = false;
195         }
196 }
197
198 void
199 Text::set_alignment (Pango::Alignment alignment)
200 {
201         begin_change ();
202         
203         _alignment = alignment;
204         _need_redraw = true;
205         _bounding_box_dirty = true;
206         end_change ();
207 }
208
209 void
210 Text::set_font_description (Pango::FontDescription font_description)
211 {
212         begin_change ();
213         
214         _font_description = new Pango::FontDescription (font_description);
215         _need_redraw = true;
216
217         _bounding_box_dirty = true;
218         end_change ();
219 }
220
221 void
222 Text::set_color (Color color)
223 {
224         begin_change ();
225
226         _color = color;
227         if (_outline) {
228                 set_outline_color (contrasting_text_color (_color));
229         }
230         _need_redraw = true;
231
232         end_change ();
233 }
234
235                 
236 void
237 Text::dump (ostream& o) const
238 {
239         Item::dump (o);
240
241         o << _canvas->indent() << '\t' << " text = " << _text << endl
242           << _canvas->indent() << " color = " << _color;
243
244         o << endl;
245 }