Merge branch 'devel'
[aoi.git] / src / gui_dicview.cxx
blob022c55ce27a3dd9d2747d403ed8432f1b83e94fa
1 /*
2 Copyright 2013 Karel Matas
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 3 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, see <http://www.gnu.org/licenses/>.
17 #include <cstdio>
18 #include <cmath>
19 #include <FL/Fl_Menu_Item.H>
20 #include "gui_dicview.hxx"
21 #include "utils.hxx"
22 #include "logger.hxx"
23 #include <stdexcept>
25 using utils::split_string;
26 using utils::TextTag;
28 namespace aoi_ui {
30 DicView::DicView ( int x, int y, int w, int h, const char *l ): Fl_Table_Row(x,y,w,h,l)
32 when( FL_WHEN_RELEASE|FL_WHEN_CHANGED );
33 table_box( FL_NO_BOX );
34 type(SELECT_SINGLE);
35 cb_leftclick( scb_general, nullptr );
36 cb_rightclick( scb_general, nullptr );
37 cb_doubleclick( scb_general, nullptr );
38 cb_select( scb_general, nullptr );
39 register_tag("default", TextStyle());;
40 register_tag("sep", TextStyle(TextStyle::SEPARATOR_SQUARE) );
41 register_tag("br", TextStyle(TextStyle::NEWLINE_AFTER));
42 cols(3);
43 headers_ = {"", "", ""};
44 col_header(1);
45 col_resize(1);
46 col_resize_min(4);
50 void DicView::set_data ( const vector<string> &d, const vector<int> &cell_ids )
52 // prevents division by zero
53 if ( cols() == 0 )
54 cols(1);
55 // clear view
56 data_.clear();
57 ids_.clear();
58 redraw();
59 // set new data
60 int c = d.size()/cols();
61 rows( (c>1) ? c:1 );
62 cell_heights_.clear();
63 cell_heights_.resize(d.size());
64 data_ = d;
65 ids_ = cell_ids;
66 if ( cell_ids.empty() )
67 ids_ = vector<int>(data_.size(),-1);
68 // recalc cols and rows and redraw
69 draw_cell( CONTEXT_RC_RESIZE, 0, 0, 0, 0 );
70 redraw();
74 int DicView::draw_single_cell ( int R, int C, int X, int Y, int W, int H )
76 int cell = R*cols()+C;
78 if ( cell >= int(data_.size()) ){
79 fl_push_clip(X, Y, W, H);
81 // background
82 fl_color( Fl::get_color( FL_BACKGROUND2_COLOR ) );
83 fl_rectf(X, Y, W, H);
84 // border
85 fl_color(FL_LIGHT2);
86 fl_rect(X, Y, W, H);
88 fl_pop_clip();
89 return -1;
92 int fy = 0;
94 fl_push_clip(X, Y, W, H);
96 // BG COLOR
97 fl_color( row_selected(R) ? FL_LIGHT2:FL_BACKGROUND2_COLOR );
98 fl_rectf(X, Y, W, H);
100 // TEXT
101 // parse data_[cell]
102 auto p = utils::parse_markup(data_.at(cell).c_str());
103 string raw_string = p.first;
104 auto tags = p.second;
105 std::sort( tags.begin(), tags.end() );
106 std::multimap<int,TextTag> tmap;
108 // fill gaps in tag ranges with default tag
109 for ( size_t i=0; i<tags.size(); i++ ) {
110 int prev_end = (i>0) ? tags[i-1].pos + tags[i-1].len:0;
111 if ( prev_end < tags[i].pos )
112 tmap.insert( { prev_end, {"default", prev_end, tags[i].pos-prev_end,
113 static_cast<int>(tmap.size()+1)} } );
114 tmap.insert( {tags[i].pos, tags[i]} );
118 // apply default tag on whole string (if no other tags were defined)
119 int raw_len = strlen(raw_string.c_str());
120 if ( tmap.empty() )
121 tmap.insert( { 0,{ "default", 0, raw_len, -1 } } );
122 // create last default tag if neccessary
123 else {
124 int pos = tags.back().pos + tags.back().len;
125 if ( pos < raw_len )
126 tmap.insert( { pos,{ "default", pos, raw_len-pos,
127 static_cast<int>(tmap.size()+1) }} );
130 for ( auto mi = tmap.begin(); mi != tmap.end(); ++mi ){
131 auto t = mi->second;
132 // remove unneded default
133 if ( t.tag == "default" ){
134 auto bounds = tmap.equal_range( mi->second.pos );
135 for( auto j=bounds.first; j!=bounds.second; j++ ){
136 // XXX: ? nemuze se stat, ze bude 2x stejny default
137 if ( j->second.pos == t.pos && j->second.len == t.len
138 && j->second.tag != "default" )
139 tmap.erase( mi );
144 tags.clear();
145 for ( auto t: tmap )
146 tags.push_back( t.second );
147 // XXX: ? sort tags by pos and id ? (torikaeru)
148 // std::sort( tags.begin(), tags.end(),
149 // []( const TextTag &a, const TextTag &b )
150 // {
151 // char ba[8], bb[8];
152 // sprintf(ba,"%d.%d",a.pos,a.id);
153 // sprintf(bb,"%d.%d",b.pos,b.id);
154 // return atof(ba)<atof(bb); }
155 // );
159 printf("\n\n************************************\n\n");
160 printf("%s\n",data_.at(cell).c_str());
161 printf("%s\n", raw_string.c_str() );
162 for ( auto t: tags ){
163 printf("[%3d] %10s pos: %2d len: %2d id: %2d '%s'\n",
164 t.pos, t.tag.c_str(), t.pos, t.len, t.id,
165 raw_string.substr(t.pos,t.len).c_str() );
169 // detect biggest font on the first line
170 // for the first line is better use font size instead of fl_height
171 // because the bigger font is, the bigger fl_height which leads to too
172 // big gaps
173 // (e.g. size=14 : fl_height=16, size=50 : fl_height=61)
174 int total_w = 0;
175 auto mi = tmap.begin();
176 int fh = 0;
177 while ( total_w < W ){
178 if ( mi == tmap.end() )
179 break;
180 TextStyle *style = get_style(mi->second.tag);
181 fl_font( style->font, int(font_size_*style->relsize) );
182 int ffh = style->relsize*font_size_;
183 if ( ffh > fh )
184 fh = ffh;
185 total_w += fl_width("m")*mi->second.len;
186 mi++;
189 // set values needed for calculations
190 int fx = X + cell_padding_x();
191 fy = Y + cell_padding_y() + fh;
193 for ( auto t: tags ){
194 // printf("%10s pos: %2zd len: %2zd '%s'\n",
195 // t.tag.c_str(), t.pos, t.len,raw_string.substr(t.pos,t.len).c_str() );
196 TextStyle *style = get_style(t.tag);
197 fl_font( style->font, int(font_size_*style->relsize) );
198 fl_color( style->color );
199 fh = fl_height();
200 int space_width = fl_width(" ");
201 int fy_correction = 0; // interline spacing when using subscript
203 // separator
204 // inserts: [SPACE]SQUARE[SPACE]
205 if ( style->separator != TextStyle::SEPARATOR_NONE ){
206 Fl_Color prev_color = fl_color();
207 fl_color( style->color );
208 int size = ceil(fh*0.4);
209 fl_rectf( fx + space_width, fy-size, size, size );
210 fx += size + 2*space_width;
211 fl_color( prev_color );
212 continue;
215 // interline space when <sub>
216 if ( style->offset_y > fy_correction )
217 fy_correction = style->offset_y;
220 // draw words
221 for ( auto &ss: split_string( raw_string.substr(t.pos,t.len), " " ) ) {
222 int swidth = fl_width(ss.c_str());
223 // break line
224 if ( fx+swidth-X > W-2*cell_padding_x() ){
225 fx = X + cell_padding_x();
226 fy += fh + fy_correction;
227 fy_correction = 0;
229 fl_draw( ss.c_str(), fx, fy + style->offset_y );
230 fx += swidth + space_width;
233 if ( style->newline == TextStyle::NEWLINE_AFTER ){
234 fx = X + cell_padding_x();
235 fy += fh + 2*fl_descent() + fy_correction;
236 fy_correction = 0;
238 } // END: for tag in tags
239 // END: TEXT
241 fy += cell_padding_y_; // empty space
243 // draw a line between rows (draw line above current cell)
244 if ( R > 0 ){
245 fl_color( Fl::get_color( FL_FOREGROUND_COLOR ) );
246 fl_line(X,Y,X+col_width(C),Y);
250 fl_pop_clip();
252 int this_height = fy-Y+cell_padding_y();
253 cell_heights_[R*C+C] = this_height;
255 if ( C == cols()-1 ){
256 int max_height = 0;
257 for ( int c=0; c<=C; c++ ){
258 int ch = cell_heights_[R*c+c];
259 if ( ch > max_height )
260 max_height = ch;
262 if ( max_height != H ){
263 redraw_row(R,max_height);
267 return this_height;
271 void DicView::redraw_row ( int R, int H )
273 row_height(R,H);
274 for ( int c=0; c<cols(); c++ ){
275 int X = 0;
276 int Y = 0;
277 int W = 0;
278 int H = 0;
279 find_cell( CONTEXT_CELL, R, c, X, Y, W, H);
280 draw_single_cell(R,c,X,Y,W,H);
286 void DicView::draw_cell ( TableContext context, int R, int C, int X, int Y, int W, int H )
288 switch ( context )
290 case CONTEXT_STARTPAGE:
291 fl_font( FL_HELVETICA, font_size());
292 return;
294 case CONTEXT_COL_HEADER:
295 fl_push_clip(X, Y, W, H);
297 fl_draw_box(FL_THIN_UP_BOX, X, Y, W, H, col_header_color());
298 fl_font( FL_HELVETICA, 0.7*col_header_height() );
299 fl_color( FL_BLACK );
300 if ( !headers_.at(C).empty() )
301 fl_draw( headers_.at(C).c_str(), X+5, Y+fl_height() );
303 fl_pop_clip();
304 return;
307 case CONTEXT_CELL:
309 draw_single_cell(R,C,X,Y,W,H);
310 return;
314 case CONTEXT_RC_RESIZE:
316 // adjust last column (sense) width
317 int sum = 0;
318 for ( int c=0; c<cols()-1; c++ )
319 sum+=col_width(c);
320 int scroll = 1.2*vscrollbar->w();
321 if ( !vscrollbar->visible() )
322 scroll = 5;
323 col_width(cols()-1, w() - sum - scroll );
324 return;
327 case CONTEXT_ROW_HEADER:
328 case CONTEXT_ENDPAGE:
329 case CONTEXT_TABLE:
330 case CONTEXT_NONE:
331 default:
332 return;
336 int DicView::handle ( int event )
338 switch ( event ) {
339 case FL_FOCUS:
341 if ( selected_row() == -1 )
342 select_row(0);
343 return 1;
345 case FL_PUSH:
347 int row = row_at_y(Fl::event_y()-y());
348 take_focus();
349 select_row(row);
350 // Fl::belowmouse() ensures that scrollbar will be still usable
351 if ( Fl::event_button() == FL_LEFT_MOUSE && Fl::belowmouse()==this ){
352 if ( Fl::event_clicks() > 1 )
353 cb_doubleclick(this);
354 else
355 cb_leftclick(this);
356 // return 1;
358 else if ( Fl::event_button() == FL_RIGHT_MOUSE ){
359 cb_rightclick(this);
360 // return 1;
363 case FL_RELEASE:
365 if ( Fl::event_button() == FL_RIGHT_MOUSE )
366 return 1;
368 case FL_KEYDOWN:
370 int row = selected_row();
371 int r1 = 0;
372 int r2 = 0;
373 int c1 = 0;
374 int c2 = 0;
375 visible_cells(r1,r2,c1,c2);
376 if ( row >= r2 )
377 top_row(r1+1);
378 else if ( row <= r1 && r1 > 0 )
379 top_row(r1-1);
381 switch ( Fl::event_key() ){
382 case FL_Down:
384 if ( row < rows() )
385 select_row(row+1);
386 return 1;
388 case FL_Up:
390 if ( row > 0 )
391 select_row(row-1);
392 return 1;
394 // Page Up/Down are handled by the vertical scrollbar
395 case ' ':
396 case FL_Enter:
397 cb_rightclick(this);
398 return 1;
402 return Fl_Table_Row::handle(event);
406 } // namespace