2 * Copyright (C) 2009 by Aiwota Programmer
3 * aiwotaprog@tetteke.tk
5 * This file is part of Dialektos.
7 * Dialektos is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * Dialektos is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Dialektos. If not, see <http://www.gnu.org/licenses/>.
21 #include "text_element_plain.hxx"
23 #include <pangomm/layout.h>
24 #include <pangomm/attributes.h>
25 #include <pangomm/attrlist.h>
26 #include <pangomm/context.h>
27 #include <pangomm/font.h>
28 #include <pangomm/rectangle.h>
29 #include <pangomm/item.h>
30 #include <pangomm/glyphstring.h>
31 #include <glibmm/refptr.h>
32 #include <glibmm/ustring.h>
34 #include <gdkmm/cursor.h>
35 #include <gdkmm/rectangle.h>
36 #include <boost/foreach.hpp>
37 #include <boost/algorithm/string.hpp>
38 #include <boost/lambda/lambda.hpp>
39 #include <boost/array.hpp>
40 #include "text_view_drawing_set.hxx"
41 #include "text_element_char_size_cache.hxx"
46 namespace text_element {
49 /* static data members */
50 CharSizeCache Plain::char_size_cache;
53 double Plain::get_approximate_char_height(const Pango::FontMetrics& metrics) {
55 metrics.get_ascent() +
56 metrics.get_descent())
60 void Plain::trim_right() {
61 using namespace boost::lambda;
63 boost::trim_right_if(str_, _1 == ' ');
66 Glib::ustring Plain::get_selected_text(
67 const text_view::GetSelectedSet& set) const {
68 size_t start_index = 0;
70 get_selection_start_end_index(set.start_element, set.start_index,
71 set.end_element, set.end_index, start_index, end_index);
72 return str_.substr(start_index, end_index-start_index);
75 void Plain::layout(text_view::LayoutSet& set) {
77 /* caching for drawing */
80 x_start_ = set.x_start;
83 BOOST_FOREACH(const Pango::Item& item, items_) {
84 Glib::RefPtr<Pango::Font> font = item.get_analysis().get_font();
85 const int font_id = reinterpret_cast<long int>(font->gobj());
86 BOOST_FOREACH(const gunichar& uch, item.get_segment(str_)) {
87 const double width = char_size_cache.get_char_width(uch, item, font_id);
88 if (set.x + width > x_end_) {
90 set.y += get_approximate_char_height(metrics_);
97 void Plain::draw(text_view::DrawingSet& set) const {
98 size_t start_index = 0;
100 get_selection_start_end_index(set.start_element, set.start_index,
101 set.end_element, set.end_index, start_index, end_index);
106 size_t str_count = 0;
107 size_t str_start = 0;
109 BOOST_FOREACH(Pango::Item item, items_) {
110 const Glib::ustring sub = item.get_segment(str_);
111 double draw_start_x = x;
112 double draw_start_y = y;
114 bool need_draw = false;
116 BOOST_FOREACH(const gunichar& uch, sub) {
117 const double width = char_size_cache.get_char_width(uch, item);
118 if (x + width > x_end_) {
121 y += get_approximate_char_height(metrics_);
124 if (str_count == start_index && str_count != end_index)
126 if (str_count == end_index && str_count != start_index)
132 do_draw(set, item, item.shape(text), draw_start_x, draw_start_y,
133 str_start >= start_index && str_count <= end_index);
135 str_start = str_count;
148 do_draw(set, item, item.shape(text), draw_start_x, draw_start_y,
149 str_start >= start_index && str_count <= end_index);
155 void Plain::do_draw(text_view::DrawingSet& set,
156 const Pango::Item& item, const Pango::GlyphString& glyphs,
157 double x, double y, bool in_selection) const {
159 // draw a rectangle for a selected region background.
161 set.window->draw_rectangle(
162 set.style->get_base_gc(Gtk::STATE_SELECTED),
163 true, std::ceil(x), y - set.adj_value,
164 std::ceil(double(glyphs.get_width()) / Pango::SCALE),
165 (metrics_.get_ascent() + metrics_.get_descent()) / Pango::SCALE);
168 do_draw_glyphs(set, item, glyphs, x, y, in_selection);
171 void Plain::do_draw_glyphs(text_view::DrawingSet& set,
172 const Pango::Item& item, const Pango::GlyphString& glyphs,
173 double x, double y, bool in_selection) const {
175 Glib::RefPtr<const Gdk::GC> gc = set.style->get_text_gc(
176 in_selection ? Gtk::STATE_SELECTED : Gtk::STATE_NORMAL);
178 set.window->draw_glyphs(gc, item.get_analysis().get_font(),
180 y - set.adj_value + metrics_.get_ascent() / Pango::SCALE, glyphs);
183 Pango::FontMetrics Plain::get_metrics() const {
188 bool Plain::is_xy_on_this(const gdouble x, const gdouble y) const {
193 BOOST_FOREACH(Pango::Item item, items_) {
194 BOOST_FOREACH(const gunichar& uch, item.get_segment(str_)) {
195 const double w = char_size_cache.get_char_width(uch, item);
197 if (x_pos + w > x_end_) {
200 y_pos += get_approximate_char_height(metrics_);
203 if (x >= x_pos && x < x_pos + w &&
204 y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
215 bool Plain::is_xy_near_to_this(const gdouble x, const gdouble y) const {
219 BOOST_FOREACH(Pango::Item item, items_) {
220 BOOST_FOREACH(const gunichar& uch, item.get_segment(str_)) {
221 const double w = char_size_cache.get_char_width(uch, item);
223 if (x_pos + w > x_end_) {
226 if (x >= x_pos && y >= y_pos &&
227 y < y_pos + get_approximate_char_height(metrics_)) {
228 // (x, y) is on the spaces caused by wrapping
233 y_pos += get_approximate_char_height(metrics_);
236 if (x >= x_pos && x < x_pos + w &&
237 y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
249 Gdk::Rectangle Plain::xy_to_rectangle(gdouble x, gdouble y) const {
252 BOOST_FOREACH(Pango::Item item, items_) {
253 BOOST_FOREACH(const gunichar& uch, item.get_segment(str_)) {
254 const double w = char_size_cache.get_char_width(uch, item);
256 if (x_pos + w > x_end_) {
259 y_pos += get_approximate_char_height(metrics_);
262 if (x >= x_pos && x < x_pos + w &&
263 y >= y_pos && y < y_pos + get_approximate_char_height(metrics_))
264 return Gdk::Rectangle(
265 x_pos, y_pos, w, get_approximate_char_height(metrics_));
271 return Gdk::Rectangle(
272 x_pos, y_pos, 0, get_approximate_char_height(metrics_));
276 int Plain::xy_to_index(const gdouble x, const gdouble y) const {
282 BOOST_FOREACH(Pango::Item item, items_) {
283 BOOST_FOREACH(const gunichar& uch, item.get_segment(str_)) {
284 const double w = char_size_cache.get_char_width(uch, item);
286 if (x_pos + w > x_end_) {
289 y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
290 // (x, y) is on the spaces caused by wrapping
295 y_pos += get_approximate_char_height(metrics_);
298 if (x >= x_pos && x < x_pos + w &&
299 y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
300 if (x <= x_pos + w/2) {
301 // left of this character
304 // right of this character
316 Gdk::CursorType Plain::get_cursor_type() const {
320 void Plain::itemize(const Glib::RefPtr<Pango::Context>& context,
321 const Pango::FontMetrics& metrics) {
322 Pango::AttrList list = Pango::AttrList();
323 Pango::AttrInt attr = bold_ ?
324 Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD):
325 Pango::Attribute::create_attr_weight(Pango::WEIGHT_NORMAL);
327 if (!str_.empty()) items_ = context->itemize(str_, list);
331 void Plain::get_selection_start_end_index(
332 const Plain* const start_element, const size_t start_index,
333 const Plain* const end_element, const size_t end_index,
334 size_t& start, size_t& end) const {
338 if (start_element && end_element) {
339 if (start_element == this && end_element == this) {
342 } else if (start_element == this) {
345 } else if (end_element == this) {
347 } else if (start_element->y_ < y_ && end_element->y_ > y_) {
349 } else if (start_element->y_ == y_ && end_element->y_ == y_ &&
350 start_element->x_ < x_ && end_element->x_ > x_) {
352 } else if (start_element->y_ == y_ && end_element->y_ != y_ &&
353 start_element->x_ < x_) {
355 } else if (end_element->y_ == y_ && start_element->y_ != y_ &&
356 end_element->x_ > x_) {
364 } // namespace text_element
366 } // namespace dialektos