OSDN Git Service

Initial commit. Text view widget is implemented.
[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 <glibmm/refptr.h>
30 #include <glibmm/ustring.h>
31 #include <gdkmm/gc.h>
32 #include <gdkmm/cursor.h>
33 #include <gdkmm/rectangle.h>
34 #include <boost/foreach.hpp>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/lambda/lambda.hpp>
37 #include <boost/array.hpp>
38 #include "text_view_drawing_set.hxx"
39 #include "text_element_char_size_cache.hxx"
40
41
42 namespace dialektos {
43
44 namespace text_element {
45
46
47 /* static data members */
48 CharSizeCache Plain::char_size_cache;
49 gdouble Plain::approximate_char_height = -1;
50
51
52 double Plain::get_approximate_char_height(
53     Glib::RefPtr<const Pango::Layout> layout) {
54
55   if (approximate_char_height < 0) {
56     Glib::RefPtr<Pango::Context> context = layout->get_context();
57     Pango::FontDescription desc = context->get_font_description();
58     Glib::RefPtr<Pango::Font> font = context->load_font(desc);
59     Pango::Rectangle rect = font->get_glyph_logical_extents(0);
60     approximate_char_height = double(rect.get_height())/Pango::SCALE*1.2;
61   }
62   return approximate_char_height;
63 }
64
65 void Plain::trim_right() {
66   using namespace boost::lambda;
67
68   boost::trim_right_if(str_, _1 == ' ');
69 }
70
71 void Plain::layout(text_view::LayoutSet& set) {
72
73   /* caching for drawing */
74   x_ = set.x;
75   y_ = set.y;
76   x_start_ = set.x_start;
77   x_end_ = set.x_end;
78
79   Pango::AttrList list = Pango::AttrList();
80   Pango::AttrInt attr = bold_ ?
81       Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD):
82       Pango::Attribute::create_attr_weight(Pango::WEIGHT_NORMAL);
83   list.insert(attr);
84   set.layout->set_attributes(list);
85
86   BOOST_FOREACH(const gunichar& uch, str_) {
87     double w, h;
88     char_size_cache.get_char_size(uch, set.layout, w, h, bold_);
89
90     if (set.x + w > x_end_) {
91       // begin new line
92       set.x = x_start_;
93       set.y += get_approximate_char_height(set.layout);
94     }
95
96     set.x += w;
97   }
98 }
99
100 void Plain::draw(text_view::DrawingSet& set) const {
101
102   Glib::RefPtr<const Gdk::GC> gc = set.style->get_text_gc(Gtk::STATE_NORMAL);
103
104   Pango::AttrList list = Pango::AttrList();
105   Pango::AttrInt attr = bold_ ?
106       Pango::Attribute::create_attr_weight(Pango::WEIGHT_BOLD):
107       Pango::Attribute::create_attr_weight(Pango::WEIGHT_NORMAL);
108   list.insert(attr);
109
110   set_attributes(list);
111   set.layout->set_attributes(list);
112
113   size_t start_index = 0;
114   size_t end_index = 0;
115   get_selection_start_end_index(set.start_element, set.start_index,
116       set.end_element, set.end_index, start_index, end_index);
117
118   gdouble x = x_;
119   gdouble y = y_;
120   size_t index_count = 0;
121   BOOST_FOREACH(const gunichar& uch, str_) {
122     double w, h;
123     char_size_cache.get_char_size_from_cache(uch, w, h, bold_);
124
125     if (x + w > x_end_) {
126       // begin new line
127       x = x_start_;
128       y += approximate_char_height;//(set.layout);
129     }
130
131     double deltaY = approximate_char_height - h;
132     set.layout->set_text(Glib::ustring(1, uch));
133
134     if (start_index != end_index &&
135         index_count >= start_index && index_count < end_index)
136       set.window->draw_layout(gc, x, y + deltaY - set.adj_value,
137           set.layout,
138           set.style->get_fg(Gtk::STATE_SELECTED),
139           set.style->get_bg(Gtk::STATE_SELECTED));
140     else
141       set.window->draw_layout(gc, x, y + deltaY - set.adj_value,
142           set.layout);
143
144     x += w;
145     ++index_count;
146   }
147 }
148
149
150 void Plain::set_attributes(Pango::AttrList& list) const {
151 }
152
153 bool Plain::is_xy_on_this(const gdouble x, const gdouble y) const {
154
155   gdouble x_pos = x_;
156   gdouble y_pos = y_;
157
158   BOOST_FOREACH(const gunichar& uch, str_) {
159     double w, h;
160     char_size_cache.get_char_size_from_cache(uch, w, h, bold_);
161
162     if (x_pos + w > x_end_) {
163       // begin new line
164       x_pos = x_start_;
165       y_pos += approximate_char_height;
166     }
167
168     if (x >= x_pos && x < x_pos + w &&
169         y >= y_pos && y < y_pos + approximate_char_height) {
170       return true;
171     }
172
173     x_pos += w;
174   }
175
176   return false;
177 }
178
179 bool Plain::is_xy_near_to_this(const gdouble x, const gdouble y) const {
180   gdouble x_pos = x_;
181   gdouble y_pos = y_;
182
183   BOOST_FOREACH(const gunichar& uch, str_) {
184     double w, h;
185     char_size_cache.get_char_size_from_cache(uch, w, h, bold_);
186
187     if (x_pos + w > x_end_) {
188       // begin new line
189
190       if (x >= x_pos && y >= y_pos && y < y_pos + approximate_char_height) {
191         // (x, y) is on the spaces caused by wrapping
192         return true;
193       }
194
195       x_pos = x_start_;
196       y_pos += approximate_char_height;
197     }
198
199     if (x >= x_pos && x < x_pos + w &&
200         y >= y_pos && y < y_pos + approximate_char_height) {
201       return true;
202     }
203
204     x_pos += w;
205   }
206
207   return false;
208 }
209
210
211 Gdk::Rectangle Plain::xy_to_rectangle(gdouble x, gdouble y) const {
212   gdouble x_pos = x_;
213   gdouble y_pos = y_;
214   BOOST_FOREACH(const gunichar& uch, str_) {
215     double w, h;
216     char_size_cache.get_char_size_from_cache(uch, w, h, bold_);
217
218     if (x_pos + w > x_end_) {
219       // begin new line
220       x_pos = x_start_;
221       y_pos += approximate_char_height;
222     }
223
224     if (x >= x_pos && x < x_pos + w &&
225         y >= y_pos && y < y_pos + approximate_char_height)
226         return Gdk::Rectangle(x_pos, y_pos, w, approximate_char_height);
227
228     x_pos += w;
229   }
230
231   return Gdk::Rectangle(x_pos, y_pos, 0, approximate_char_height);
232 }
233
234
235 int Plain::xy_to_index(const gdouble x, const gdouble y) const {
236
237   gdouble x_pos = x_;
238   gdouble y_pos = y_;
239   int index = 0;
240
241   BOOST_FOREACH(const gunichar& uch, str_) {
242     double w, h;
243     char_size_cache.get_char_size_from_cache(uch, w, h, bold_);
244
245     if (x_pos + w > x_end_) {
246       // begin new line
247       if (x >= x_pos &&
248           y >= y_pos && y < y_pos + approximate_char_height) {
249         // (x, y) is on the spaces caused by wrapping
250         return index;
251       }
252
253         x_pos = x_start_;
254       y_pos += approximate_char_height;
255     }
256
257     if (x >= x_pos && x < x_pos + w &&
258         y >= y_pos && y < y_pos + approximate_char_height) {
259       if (x <= x_pos + w/2) {
260         // left of this character
261         return index;
262       } else {
263         // right of this character
264         return index+1;
265       }
266     }
267
268     x_pos += w;
269     ++index;
270   }
271   return index;
272 }
273
274 Gdk::CursorType Plain::get_cursor_type() const {
275   return Gdk::XTERM;
276 }
277
278 void Plain::get_selection_start_end_index(
279     const Plain* const start_element, const size_t start_index,
280     const Plain* const end_element, const size_t end_index,
281     size_t& start, size_t& end) const {
282
283   start = 0;
284   end = 0;
285   if (start_element && end_element) {
286     if (start_element == this && end_element == this) {
287       start = start_index;
288       end = end_index;
289     } else if (start_element == this) {
290       start = start_index;
291       end = str_.size();
292     } else if (end_element == this) {
293       end = end_index;
294     } else if (start_element->y_ < y_ && end_element->y_ > y_) {
295       end = str_.size();
296     } else if (start_element->y_ == y_ && end_element->y_ == y_ &&
297         start_element->x_ < x_ && end_element->x_ > x_) {
298       end = str_.size();
299     } else if (start_element->y_ == y_ && end_element->y_ != y_ &&
300         start_element->x_ < x_) {
301       end = str_.size();
302     } else if (end_element->y_ == y_ && start_element->y_ != y_ &&
303         end_element->x_ > x_) {
304       end = str_.size();
305     }
306   }
307
308 }
309
310
311 } // namespace text_element
312
313 } // namespace dialektos