OSDN Git Service

AMD64 issues.
[fukui-no-namari/dialektos.git] / src / text_element_plain.cxx
1 /*
2  * Copyright (C) 2009 by Aiwota Programmer
3  * aiwotaprog@tetteke.tk
4  *
5  * This file is part of Dialektos.
6  *
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.
11  *
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.
16  *
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/>.
19  */
20
21 #include "text_element_plain.hxx"
22
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>
33 #include <gdkmm/gc.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"
42
43
44 namespace dialektos {
45
46 namespace text_element {
47
48
49 /* static data members */
50 CharSizeCache Plain::char_size_cache;
51
52
53 double Plain::get_approximate_char_height(const Pango::FontMetrics& metrics) {
54   return double(
55       metrics.get_ascent() +
56       metrics.get_descent())
57       / Pango::SCALE * 1.2;
58 }
59
60 void Plain::trim_right() {
61   using namespace boost::lambda;
62
63   boost::trim_right_if(str_, _1 == ' ');
64 }
65
66 Glib::ustring Plain::get_selected_text(
67     const text_view::GetSelectedSet& set) const {
68   size_t start_index = 0;
69   size_t end_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);
73 }
74
75 void Plain::layout(text_view::LayoutSet& set) {
76
77   /* caching for drawing */
78   x_ = set.x;
79   y_ = set.y;
80   x_start_ = set.x_start;
81   x_end_ = set.x_end;
82
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_) {
89         set.x = x_start_;
90         set.y += get_approximate_char_height(metrics_);
91       }
92       set.x += width;
93     }
94   }
95 }
96
97 void Plain::draw(text_view::DrawingSet& set) const {
98   size_t start_index = 0;
99   size_t end_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);
102
103   double x = x_;
104   double y = y_;
105
106   size_t str_count = 0;
107   size_t str_start = 0;
108
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;
113     Glib::ustring text;
114     bool need_draw = false;
115
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_) {
119         need_draw = true;
120         x = x_start_;
121         y += get_approximate_char_height(metrics_);
122       }
123
124       if (str_count == start_index && str_count != end_index)
125         need_draw = true;
126       if (str_count == end_index && str_count != start_index)
127         need_draw = true;
128
129       if (need_draw) {
130
131         if (!text.empty())
132           do_draw(set, item, item.shape(text), draw_start_x, draw_start_y,
133               str_start >= start_index && str_count <= end_index);
134
135         str_start = str_count;
136         draw_start_x = x;
137         draw_start_y = y;
138         text.clear();
139         need_draw = false;
140       }
141
142       x += width;
143       ++str_count;
144       text += uch;
145     }
146
147     if (!text.empty())
148       do_draw(set, item, item.shape(text), draw_start_x, draw_start_y,
149           str_start >= start_index && str_count <= end_index);
150
151   }
152 }
153
154
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 {
158
159   // draw a rectangle for a selected region background.
160   if (in_selection) {
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);
166   }
167
168   do_draw_glyphs(set, item, glyphs, x, y, in_selection);
169 }
170
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 {
174
175   Glib::RefPtr<const Gdk::GC> gc = set.style->get_text_gc(
176       in_selection ? Gtk::STATE_SELECTED : Gtk::STATE_NORMAL);
177
178   set.window->draw_glyphs(gc, item.get_analysis().get_font(),
179       std::ceil(x),
180       y - set.adj_value + metrics_.get_ascent() / Pango::SCALE, glyphs);
181 }
182
183 Pango::FontMetrics Plain::get_metrics() const {
184   return metrics_;
185 }
186
187
188 bool Plain::is_xy_on_this(const gdouble x, const gdouble y) const {
189
190   gdouble x_pos = x_;
191   gdouble y_pos = y_;
192
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);
196
197       if (x_pos + w > x_end_) {
198         // begin new line
199         x_pos = x_start_;
200         y_pos += get_approximate_char_height(metrics_);
201       }
202
203       if (x >= x_pos && x < x_pos + w &&
204           y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
205         return true;
206       }
207
208       x_pos += w;
209     }
210   }
211
212   return false;
213 }
214
215 bool Plain::is_xy_near_to_this(const gdouble x, const gdouble y) const {
216   gdouble x_pos = x_;
217   gdouble y_pos = y_;
218
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);
222
223       if (x_pos + w > x_end_) {
224         // begin new line
225
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
229           return true;
230         }
231
232         x_pos = x_start_;
233         y_pos += get_approximate_char_height(metrics_);
234       }
235
236       if (x >= x_pos && x < x_pos + w &&
237           y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
238         return true;
239       }
240
241       x_pos += w;
242     }
243   }
244
245   return false;
246 }
247
248
249 Gdk::Rectangle Plain::xy_to_rectangle(gdouble x, gdouble y) const {
250   gdouble x_pos = x_;
251   gdouble y_pos = y_;
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);
255
256       if (x_pos + w > x_end_) {
257         // begin new line
258         x_pos = x_start_;
259         y_pos += get_approximate_char_height(metrics_);
260       }
261
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_));
266
267       x_pos += w;
268     }
269   }
270
271   return Gdk::Rectangle(
272       x_pos, y_pos, 0, get_approximate_char_height(metrics_));
273 }
274
275
276 int Plain::xy_to_index(const gdouble x, const gdouble y) const {
277
278   gdouble x_pos = x_;
279   gdouble y_pos = y_;
280   int index = 0;
281
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);
285
286       if (x_pos + w > x_end_) {
287         // begin new line
288         if (x >= x_pos &&
289             y >= y_pos && y < y_pos + get_approximate_char_height(metrics_)) {
290           // (x, y) is on the spaces caused by wrapping
291           return index;
292         }
293
294         x_pos = x_start_;
295         y_pos += get_approximate_char_height(metrics_);
296       }
297
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
302           return index;
303         } else {
304           // right of this character
305           return index+1;
306         }
307       }
308
309       x_pos += w;
310       ++index;
311     }
312   }
313   return index;
314 }
315
316 Gdk::CursorType Plain::get_cursor_type() const {
317   return Gdk::XTERM;
318 }
319
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);
326   list.insert(attr);
327   if (!str_.empty()) items_ = context->itemize(str_, list);
328   metrics_ = metrics;
329 }
330
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 {
335
336   start = 0;
337   end = 0;
338   if (start_element && end_element) {
339     if (start_element == this && end_element == this) {
340       start = start_index;
341       end = end_index;
342     } else if (start_element == this) {
343       start = start_index;
344       end = str_.size();
345     } else if (end_element == this) {
346       end = end_index;
347     } else if (start_element->y_ < y_ && end_element->y_ > y_) {
348       end = str_.size();
349     } else if (start_element->y_ == y_ && end_element->y_ == y_ &&
350         start_element->x_ < x_ && end_element->x_ > x_) {
351       end = str_.size();
352     } else if (start_element->y_ == y_ && end_element->y_ != y_ &&
353         start_element->x_ < x_) {
354       end = str_.size();
355     } else if (end_element->y_ == y_ && start_element->y_ != y_ &&
356         end_element->x_ > x_) {
357       end = str_.size();
358     }
359   }
360
361 }
362
363
364 } // namespace text_element
365
366 } // namespace dialektos