1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #include <Elementary.h>
6 
7 #include "elm_priv.h"
8 
9 #include "elm_code_private.h"
10 #include "elm_code_widget_private.h"
11 
12 /* FIXME: hoversel is deprecated in EO APIs */
13 #include "elm_hoversel_eo.h"
14 
15 #define MY_CLASS ELM_CODE_WIDGET_CLASS
16 
17 typedef enum {
18    ELM_CODE_WIDGET_COLOR_GUTTER_BG = ELM_CODE_TOKEN_TYPE_COUNT,
19    ELM_CODE_WIDGET_COLOR_GUTTER_SCOPE_BG,
20    ELM_CODE_WIDGET_COLOR_GUTTER_FG,
21    ELM_CODE_WIDGET_COLOR_WHITESPACE,
22    ELM_CODE_WIDGET_COLOR_SELECTION,
23 
24    ELM_CODE_WIDGET_COLOR_COUNT
25 } Elm_Code_Widget_Colors;
26 
27 static Eina_Unicode status_icons[] = {
28  ' ',
29  ' ',
30  ' ',
31  ' ',
32  '!',
33  '!',
34  '!',
35 
36  '+',
37  '-',
38  ' ',
39 
40  0x2713,
41  0x2717,
42 
43  0x2691,
44 
45  0
46 };
47 
48 #define EO_CONSTRUCTOR_CHECK_RETURN(obj) do { \
49    Eina_Bool finalized; \
50    \
51    finalized = efl_finalized_get(obj); \
52    if (finalized) \
53      { \
54         ERR("This function is only allowed during construction."); \
55         return; \
56      } \
57 } while (0)
58 
59 static void _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline);
60 
61 #include "elm_code_widget_legacy_eo.h"
62 
63 EAPI Evas_Object *
elm_code_widget_add(Evas_Object * parent,Elm_Code * code)64 elm_code_widget_add(Evas_Object *parent, Elm_Code *code)
65 {
66    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
67    return elm_legacy_add(ELM_CODE_WIDGET_LEGACY_CLASS, parent,
68                          efl_ui_code_widget_code_set(efl_added, code));
69 }
70 
71 #include "elm_code_widget_legacy_eo.c"
72 
73 EOLIAN static Eo *
_elm_code_widget_efl_object_constructor(Eo * obj,Elm_Code_Widget_Data * pd)74 _elm_code_widget_efl_object_constructor(Eo *obj, Elm_Code_Widget_Data *pd)
75 {
76    obj = efl_constructor(efl_super(obj, ELM_CODE_WIDGET_CLASS));
77 
78    pd->cursor_line = 1;
79    pd->cursor_col = 1;
80 
81    pd->tabstop = 8;
82 
83    return obj;
84 }
85 
86 EOLIAN static Eo *
_elm_code_widget_efl_object_finalize(Eo * obj,Elm_Code_Widget_Data * pd)87 _elm_code_widget_efl_object_finalize(Eo *obj, Elm_Code_Widget_Data *pd)
88 {
89    obj = efl_finalize(efl_super(obj, ELM_CODE_WIDGET_CLASS));
90 
91    if (pd->code)
92      return obj;
93 
94    ERR("Elm_Code_Widget cannot finalize without calling elm_code_widget_code_set.");
95    return NULL;
96 }
97 
98 EOLIAN static void
_elm_code_widget_class_constructor(Efl_Class * klass EINA_UNUSED)99 _elm_code_widget_class_constructor(Efl_Class *klass EINA_UNUSED)
100 {
101 
102 }
103 
104 void
_elm_code_widget_cell_size_get(Elm_Code_Widget * widget,Evas_Coord * width,Evas_Coord * height)105 _elm_code_widget_cell_size_get(Elm_Code_Widget *widget, Evas_Coord *width, Evas_Coord *height)
106 {
107    Elm_Code_Widget_Data *pd;
108    Evas_Object *grid;
109    Evas_Coord w = 0, h = 0;
110 
111    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
112 
113    grid = eina_list_nth(pd->grids, 0);
114    if (!grid)
115      return;
116 
117    evas_object_textgrid_cell_size_get(grid, &w, &h);
118    if (w == 0) w = 5;
119    if (h == 0) h = 10;
120 
121    if (width) *width = w;
122    if (height) *height = h;
123 }
124 
125 static void
_elm_code_widget_scroll_by(Elm_Code_Widget * widget,int by_x,int by_y)126 _elm_code_widget_scroll_by(Elm_Code_Widget *widget, int by_x, int by_y)
127 {
128    Elm_Code_Widget_Data *pd;
129    Evas_Coord x, y, w, h;
130 
131    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
132 
133    elm_scroller_region_get(pd->scroller, &x, &y, &w, &h);
134    x += by_x;
135    y += by_y;
136    elm_scroller_region_show(pd->scroller, x, y, w, h);
137 }
138 
139 static void
_elm_code_widget_fill_line_token(Evas_Textgrid_Cell * cells,int count,int start,int end,Elm_Code_Token_Type type)140 _elm_code_widget_fill_line_token(Evas_Textgrid_Cell *cells, int count, int start, int end, Elm_Code_Token_Type type)
141 {
142    int x;
143 
144    for (x = start; x <= end && x < count; x++)
145      {
146         // TODO find a way to mark if a token is themes fg or bg
147         if (type == ELM_CODE_TOKEN_TYPE_MATCH)
148           cells[x - 1].bg = type;
149         else
150           cells[x - 1].fg = type;
151      }
152 }
153 
154 static unsigned int
_elm_code_widget_status_type_get(Elm_Code_Widget_Data * pd,Elm_Code_Line * line,unsigned int col)155 _elm_code_widget_status_type_get(Elm_Code_Widget_Data *pd, Elm_Code_Line *line, unsigned int col)
156 {
157    if (line->status != ELM_CODE_STATUS_TYPE_DEFAULT)
158      return line->status;
159 
160    if (pd->editable && pd->focused && pd->cursor_line == line->number)
161      return ELM_CODE_STATUS_TYPE_CURRENT;
162 
163    if (pd->line_width_marker > 0 && pd->line_width_marker == col-1)
164      return ELM_CODE_WIDGET_COLOR_GUTTER_BG;
165 
166    return ELM_CODE_STATUS_TYPE_DEFAULT;
167 }
168 
169 static void
_elm_code_widget_fill_line_tokens(Elm_Code_Widget * widget,Evas_Textgrid_Cell * cells,unsigned int count,Elm_Code_Line * line)170 _elm_code_widget_fill_line_tokens(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells,
171                                   unsigned int count, Elm_Code_Line *line)
172 {
173    Eina_List *item;
174    Elm_Code_Token *token;
175    unsigned int end, length, offset;
176    unsigned int token_start_col, token_end_col;
177 
178    offset = efl_ui_code_widget_text_left_gutter_width_get(widget);
179    length = elm_code_widget_line_text_column_width_get(widget, line) + offset;
180 
181    EINA_LIST_FOREACH(line->tokens, item, token)
182      {
183         token_start_col = elm_code_widget_line_text_column_width_to_position(widget, line, token->start) + offset;
184         token_end_col = elm_code_widget_line_text_column_width_to_position(widget, line, token->end) + offset;
185 
186         // TODO handle a token starting before the previous finishes
187         end = token_end_col;
188         if (token->continues)
189           end = length + offset;
190         _elm_code_widget_fill_line_token(cells, count, token_start_col, end, token->type);
191      }
192 }
193 
194 static Eina_Bool
_elm_code_widget_line_in_scope(Elm_Code_Line * line,Elm_Code_Line * fromline)195 _elm_code_widget_line_in_scope(Elm_Code_Line *line, Elm_Code_Line *fromline)
196 {
197    Elm_Code_Line *midline;
198    unsigned int number;
199 
200    if (!line || !fromline || line->scope == 0 || fromline->scope == 0)
201      return EINA_FALSE;
202 
203    if (line->number == fromline->number)
204      return EINA_TRUE;
205 
206    if (line->scope < fromline->scope)
207      return EINA_FALSE;
208 
209    number = fromline->number;
210    while (number != line->number)
211    {
212       midline = elm_code_file_line_get(line->file, number);
213 
214       if (midline->scope < fromline->scope)
215         return EINA_FALSE;
216 
217       if (line->number < fromline->number)
218         number--;
219       else
220         number++;
221    }
222    return EINA_TRUE;
223 }
224 
225 static void
_elm_code_widget_fill_line_gutter(Elm_Code_Widget * widget,Evas_Textgrid_Cell * cells,int width,Elm_Code_Line * line)226 _elm_code_widget_fill_line_gutter(Elm_Code_Widget *widget, Evas_Textgrid_Cell *cells,
227                                   int width, Elm_Code_Line *line)
228 {
229    char *number = NULL;
230    int gutter, g;
231    Elm_Code_Widget_Data *pd;
232    Elm_Code_Line *cursor_line;
233 
234    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
235    gutter = elm_code_widget_text_left_gutter_width_get(widget);
236 
237    if (width < gutter)
238      return;
239 
240    cells[gutter-1].codepoint = status_icons[line->status];
241    cells[gutter-1].bold = 1;
242    cells[gutter-1].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
243    if (line->status == ELM_CODE_STATUS_TYPE_DEFAULT)
244      {
245         cursor_line = elm_code_file_line_get(line->file, pd->cursor_line);
246         if (_elm_code_widget_line_in_scope(line, cursor_line))
247           cells[gutter-1].bg = ELM_CODE_WIDGET_COLOR_GUTTER_SCOPE_BG;
248         else if (pd->show_line_numbers)
249           cells[gutter-1].bg = ELM_CODE_WIDGET_COLOR_GUTTER_BG;
250      }
251    else
252      {
253         cells[gutter-1].bg = line->status;
254      }
255 
256    if (pd->show_line_numbers)
257      {
258         if (line->number > 0)
259           {
260              number = malloc(sizeof(char) * gutter);
261              if (!number) return;
262              snprintf(number, gutter, "%*d", gutter - 1, line->number);
263           }
264         for (g = 0; g < gutter - 1; g++)
265           {
266              if (number)
267                cells[g].codepoint = *(number + g);
268              else
269                cells[g].codepoint = 0;
270 
271              cells[g].fg = ELM_CODE_WIDGET_COLOR_GUTTER_FG;
272              cells[g].bg = ELM_CODE_WIDGET_COLOR_GUTTER_BG;
273           }
274 
275         if (number)
276           free(number);
277      }
278 }
279 
280 static void
_elm_code_widget_fill_whitespace(Elm_Code_Widget * widget,Eina_Unicode character,Evas_Textgrid_Cell * cell)281 _elm_code_widget_fill_whitespace(Elm_Code_Widget *widget, Eina_Unicode character, Evas_Textgrid_Cell *cell)
282 {
283    Elm_Code_Widget_Data *pd;
284 
285    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
286    if (!pd->show_whitespace)
287      {
288         if (character== '\t')
289           cell->codepoint = 0;
290         return;
291      }
292 
293    switch (character)
294      {
295         case ' ':
296           cell->codepoint = 0x00b7;
297           break;
298         case '\t':
299           cell->codepoint = 0x2192;
300           break;
301         case '\n':
302           cell->codepoint = 0x23ce;
303           break;
304         default:
305           return;
306      }
307 
308    cell->fg = ELM_CODE_WIDGET_COLOR_WHITESPACE;
309 }
310 
311 static void
_elm_code_widget_cursor_update(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd)312 _elm_code_widget_cursor_update(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
313 {
314    Evas_Coord ox, oy, ow, oh;
315    Evas_Coord cx = 0, cy = 0, cw = 0, ch = 0;
316 
317    elm_code_widget_geometry_for_position_get(widget, pd->cursor_line, pd->cursor_col, &cx, &cy, &cw, &ch);
318 
319    if (!pd->cursor_rect)
320      {
321         pd->cursor_rect = elm_layout_add(widget);
322 
323         if (!elm_layout_theme_set(pd->cursor_rect, "entry", "cursor", elm_widget_style_get(widget)))
324           CRI("Failed to set layout!");
325         elm_layout_signal_emit(pd->cursor_rect, "elm,action,focus", "elm");
326      }
327 
328    evas_object_smart_calculate(pd->scroller);
329    evas_object_smart_calculate(pd->gridbox);
330    evas_object_geometry_get(widget, &ox, &oy, &ow, &oh);
331 
332    if ((cx < ox) || (cx > (ox + ow)) || (cy < oy) || (cy > (oy + oh - ch)))
333      evas_object_hide(pd->cursor_rect);
334    else
335      {
336         evas_object_geometry_set(pd->cursor_rect, cx, cy, cw/8, ch);
337         evas_object_show(pd->cursor_rect);
338      }
339 }
340 
341 static void
_elm_code_widget_fill_cursor(Elm_Code_Widget * widget,unsigned int number,int gutter,int w)342 _elm_code_widget_fill_cursor(Elm_Code_Widget *widget, unsigned int number, int gutter, int w)
343 {
344    Elm_Code_Widget_Data *pd;
345 
346    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
347 
348    if (pd->visible && pd->editable && pd->focused && pd->cursor_line == number)
349      {
350         if (pd->cursor_col + gutter - 1 >= (unsigned int) w)
351           return;
352 
353         if (pd->selection && !pd->selection->in_progress)
354           {
355              Elm_Code_Line *line = elm_code_file_line_get(pd->code->file, number);
356              if (!line)
357                return;
358 
359              if (!elm_code_widget_line_visible_get(widget, line))
360                return;
361           }
362 
363         _elm_code_widget_cursor_update(widget, pd);
364      }
365 }
366 
367 static void
_elm_code_widget_fill_selection(Elm_Code_Widget * widget,Elm_Code_Line * line,Evas_Textgrid_Cell * cells,int gutter,int w)368 _elm_code_widget_fill_selection(Elm_Code_Widget *widget, Elm_Code_Line *line, Evas_Textgrid_Cell *cells,
369                                 int gutter, int w)
370 {
371    Elm_Code_Widget_Data *pd;
372    unsigned int x, start, end;
373    Elm_Code_Widget_Selection_Data *selection;
374 
375    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
376    if (!pd->selection)
377      return;
378 
379    selection = elm_code_widget_selection_normalized_get(widget);
380    if (selection->start_line > line->number || selection->end_line < line->number)
381      {
382         free(selection);
383         return;
384      }
385 
386    start = selection->start_col;
387    if (selection->start_line < line->number)
388      start = 1;
389    end = selection->end_col;
390    if (selection->end_line > line->number)
391      end = w;
392    free(selection);
393 
394    for (x = gutter + start - 1; x < gutter + end && x < (unsigned int) w; x++)
395      cells[x].bg = ELM_CODE_WIDGET_COLOR_SELECTION;
396 }
397 
398 static void
_elm_code_widget_fill_line(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)399 _elm_code_widget_fill_line(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
400 {
401    char *chr;
402    Eina_Unicode unichr;
403    unsigned int length, x, charwidth, i, w;
404    int chrpos, gutter;
405    Evas_Object *grid;
406    Evas_Textgrid_Cell *cells;
407 
408    EINA_SAFETY_ON_NULL_RETURN(line);
409 
410    gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
411    if (eina_list_count(pd->grids) < line->number)
412      return;
413 
414    w = elm_code_widget_columns_get(widget);
415    grid = eina_list_nth(pd->grids, line->number - 1);
416    cells = evas_object_textgrid_cellrow_get(grid, 0);
417    length = elm_code_widget_line_text_column_width_get(widget, line);
418    chrpos = 0;
419    chr = (char *)elm_code_line_text_get(line, NULL);
420    for (x = gutter; x < (unsigned int) w && x < length + gutter; x+=charwidth)
421      {
422         unichr = eina_unicode_utf8_next_get(chr, &chrpos);
423 
424         cells[x].codepoint = unichr;
425         cells[x].bold = 0;
426         cells[x].fg = ELM_CODE_TOKEN_TYPE_DEFAULT;
427         cells[x].bg = _elm_code_widget_status_type_get(pd, line, x - gutter + 1);
428 
429         charwidth = 1;
430         if (unichr == '\t')
431           charwidth = elm_code_widget_text_tabwidth_at_column_get(widget, x - gutter + 1);
432         for (i = x + 1; i < x + charwidth && i < (unsigned int) w; i++)
433           {
434              cells[i].codepoint = 0;
435              cells[i].bg = _elm_code_widget_status_type_get(pd, line, i - gutter + 1);
436           }
437 
438         _elm_code_widget_fill_whitespace(widget, unichr, &cells[x]);
439      }
440    for (; x < (unsigned int) w; x++)
441      {
442         cells[x].codepoint = 0;
443         cells[x].bold = 0;
444         cells[x].bg = _elm_code_widget_status_type_get(pd, line, x - gutter + 1);
445      }
446 
447    _elm_code_widget_fill_line_gutter(widget, cells, w, line);
448    _elm_code_widget_fill_line_tokens(widget, cells, w, line);
449 
450    _elm_code_widget_fill_selection(widget, line, cells, gutter, w);
451    _elm_code_widget_fill_cursor(widget, line->number, gutter, w);
452    if (line->number < elm_code_file_lines_get(line->file))
453      _elm_code_widget_fill_whitespace(widget, '\n', &cells[length + gutter]);
454 
455    evas_object_textgrid_update_add(grid, 0, 0, w, 1);
456 }
457 
458 static void
_elm_code_widget_cursor_selection_set(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd)459 _elm_code_widget_cursor_selection_set(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd)
460 {
461    unsigned int end_line, end_col;
462 
463    end_line = pd->selection->end_line;
464    end_col = pd->selection->end_col;
465 
466    if (pd->selection->type == ELM_CODE_WIDGET_SELECTION_KEYBOARD)
467      return;
468 
469    if ((pd->selection->start_line == pd->selection->end_line && pd->selection->end_col > pd->selection->start_col) ||
470        (pd->selection->start_line < pd->selection->end_line))
471      {
472         end_col++;
473      }
474 
475    elm_code_widget_cursor_position_set(widget, end_line, end_col);
476 }
477 
478 static void
_elm_code_widget_fill_range(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int first_row,unsigned int last_row,Elm_Code_Line * newline)479 _elm_code_widget_fill_range(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
480                             unsigned int first_row, unsigned int last_row,
481                             Elm_Code_Line *newline)
482 {
483    Elm_Code_Line *line;
484    unsigned int y;
485 
486    if (newline)
487      _elm_code_widget_fill_line(widget, pd, newline);
488 
489    // if called from new line cb, no need to update whole range unless visible
490    if (newline && !efl_ui_code_widget_line_visible_get(widget, newline))
491      return;
492 
493    // cursor will be shown if it should be visible
494    evas_object_hide(pd->cursor_rect);
495 
496    for (y = first_row; y <= last_row; y++)
497      {
498         line = elm_code_file_line_get(pd->code->file, y);
499 
500         if (line)
501           _elm_code_widget_fill_line(widget, pd, line);
502      }
503 
504    if (pd->selection)
505      _elm_code_widget_cursor_selection_set(widget, pd);
506 }
507 
508 static void
_elm_code_widget_fill_update(Elm_Code_Widget * widget,unsigned int first_row,unsigned int last_row,Elm_Code_Line * newline)509 _elm_code_widget_fill_update(Elm_Code_Widget *widget, unsigned int first_row, unsigned int last_row,
510                              Elm_Code_Line *newline)
511 {
512    Elm_Code_Widget_Data *pd;
513 
514    _elm_code_widget_resize(widget, newline);
515    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
516 
517    _elm_code_widget_fill_range(widget, pd, first_row, last_row, newline);
518 }
519 
520 static Eina_Bool
_elm_code_widget_viewport_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int * first_row,unsigned int * last_row)521 _elm_code_widget_viewport_get(Elm_Code_Widget *widget,
522                               Elm_Code_Widget_Data *pd,
523                               unsigned int *first_row,
524                               unsigned int *last_row)
525 {
526    Evas_Coord scroll_y, scroll_h, oy;
527 
528    evas_object_geometry_get(widget, NULL, &oy, NULL, NULL);
529    elm_scroller_region_get(pd->scroller, NULL, &scroll_y, NULL, &scroll_h);
530    if (scroll_h == 0)
531      return EINA_FALSE;
532 
533    elm_code_widget_position_at_coordinates_get(widget, 0, oy, first_row, NULL);
534    elm_code_widget_position_at_coordinates_get(widget, 0, oy + scroll_h, last_row, NULL);
535 
536    if (last_row && *last_row > elm_code_file_lines_get(pd->code->file))
537      *last_row = elm_code_file_lines_get(pd->code->file);
538 
539    return EINA_TRUE;
540 }
541 
542 static void
_elm_code_widget_refresh(Elm_Code_Widget * widget,Elm_Code_Line * line)543 _elm_code_widget_refresh(Elm_Code_Widget *widget, Elm_Code_Line *line)
544 {
545    unsigned int first_row, last_row;
546 
547    Elm_Code_Widget_Data *pd;
548 
549    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
550    if (!_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
551      return ;
552 
553    _elm_code_widget_fill_update(widget, first_row, last_row, line);
554    if (pd->editable)
555      _elm_code_widget_cursor_update(widget, pd);
556 }
557 
558 static void
_elm_code_widget_clear(Elm_Code_Widget * widget)559 _elm_code_widget_clear(Elm_Code_Widget *widget)
560 {
561    Elm_Code_Widget_Data *pd;
562    Evas_Object *grid;
563 
564    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
565    EINA_LIST_FREE(pd->grids, grid)
566      {
567         evas_object_del(grid);
568      }
569 }
570 
571 static void
_elm_code_widget_fill(Elm_Code_Widget * widget)572 _elm_code_widget_fill(Elm_Code_Widget *widget)
573 {
574    Elm_Code_Widget_Data *pd;
575    unsigned int height;
576 
577    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
578    height = elm_code_widget_lines_visible_get(widget);
579    if (height > elm_code_file_lines_get(pd->code->file))
580      height = elm_code_file_lines_get(pd->code->file);
581 
582    _elm_code_widget_fill_update(widget, 1, height, NULL);
583 }
584 
585 static void
_elm_code_widget_line_cb(void * data,const Efl_Event * event)586 _elm_code_widget_line_cb(void *data, const Efl_Event *event)
587 {
588    Elm_Code_Line *line;
589    Elm_Code_Widget *widget;
590 
591    line = (Elm_Code_Line *)event->info;
592    widget = (Elm_Code_Widget *)data;
593 
594    _elm_code_widget_refresh(widget, line);
595 }
596 
597 static void
_elm_code_widget_file_cb(void * data,const Efl_Event * event EINA_UNUSED)598 _elm_code_widget_file_cb(void *data, const Efl_Event *event EINA_UNUSED)
599 {
600    Elm_Code_Widget *widget;
601    Elm_Code_Widget_Data *pd;
602 
603    widget = (Elm_Code_Widget *)data;
604 
605    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
606 
607    if (elm_code_file_lines_get(pd->code->file))
608      _elm_code_widget_fill(widget);
609    else
610      _elm_code_widget_clear(widget);
611 }
612 
613 static void
_elm_code_widget_selection_cb(void * data,const Efl_Event * event EINA_UNUSED)614 _elm_code_widget_selection_cb(void *data, const Efl_Event *event EINA_UNUSED)
615 {
616    Elm_Code_Widget *widget;
617 
618    widget = (Elm_Code_Widget *)data;
619 
620    _elm_code_widget_refresh(widget, NULL);
621 }
622 
623 static void
_elm_code_widget_selection_clear_cb(void * data,const Efl_Event * event EINA_UNUSED)624 _elm_code_widget_selection_clear_cb(void *data, const Efl_Event *event EINA_UNUSED)
625 {
626    Elm_Code_Widget *widget;
627 
628    widget = (Elm_Code_Widget *)data;
629 
630    _elm_code_widget_fill(widget);
631 }
632 
633 static void
_elm_code_widget_resize_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)634 _elm_code_widget_resize_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
635                            void *event_info EINA_UNUSED)
636 {
637    Elm_Code_Widget *widget;
638 
639    widget = (Elm_Code_Widget *)data;
640 
641    _elm_code_widget_refresh(widget, NULL);
642 }
643 
644 static void
_elm_code_widget_show_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)645 _elm_code_widget_show_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
646                            void *event_info EINA_UNUSED)
647 {
648    Elm_Code_Widget_Data *pd;
649    Elm_Code_Widget *widget = (Elm_Code_Widget *) data;
650 
651    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
652 
653    pd->visible = EINA_TRUE;
654    if (pd->cursor_rect)
655      evas_object_show(pd->cursor_rect);
656 }
657 
658 static void
_elm_code_widget_hidden_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)659 _elm_code_widget_hidden_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
660                            void *event_info EINA_UNUSED)
661 {
662    Elm_Code_Widget_Data *pd;
663    Elm_Code_Widget *widget = (Elm_Code_Widget *) data;
664 
665    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
666 
667    pd->visible = EINA_FALSE;
668    if (pd->cursor_rect)
669      evas_object_hide(pd->cursor_rect);
670 }
671 
672 static void
_elm_code_widget_cursor_ensure_visible(Elm_Code_Widget * widget)673 _elm_code_widget_cursor_ensure_visible(Elm_Code_Widget *widget)
674 {
675    Evas_Coord viewx, viewy, vieww, viewh, cellw = 0, cellh = 0;
676    Evas_Coord curx, cury, oy, rowy;
677    Evas_Object *grid;
678    Elm_Code_Widget_Data *pd;
679    int gutter;
680 
681    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
682 
683    evas_object_geometry_get(widget, NULL, &oy, NULL, NULL);
684    elm_scroller_region_get(pd->scroller, &viewx, &viewy, &vieww, &viewh);
685    _elm_code_widget_cell_size_get(widget, &cellw, &cellh);
686 
687    grid = eina_list_data_get(eina_list_nth_list(pd->grids, pd->cursor_line - 1));
688    evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
689 
690    gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
691    curx = (pd->cursor_col + gutter - 1) * cellw;
692    cury = rowy + viewy - oy;
693 
694    if (curx >= viewx && cury >= viewy && curx + cellw <= viewx + vieww && cury + cellh <= viewy + viewh)
695      return;
696 
697    elm_scroller_region_show(pd->scroller, curx, cury, cellw, cellh);
698 }
699 
700 static void
_elm_code_widget_cursor_move(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int col,unsigned int line)701 _elm_code_widget_cursor_move(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, unsigned int col, unsigned int line)
702 {
703    Elm_Code *code;
704    Elm_Code_Line *line_obj;
705    const char *text;
706    unsigned int oldrow, position, length, first_row, last_row;
707    int cw = 0, ch = 0;
708 
709    oldrow = pd->cursor_line;
710 
711    pd->cursor_col = col;
712    pd->cursor_line = line;
713 
714    if (line > elm_code_file_lines_get(pd->code->file))
715      return;
716 
717    if ((line > eina_list_count(pd->grids)) && (!pd->selection))
718      {
719         if (_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
720           {
721               _elm_code_widget_cell_size_get(widget, &cw, &ch);
722               _elm_code_widget_scroll_by(widget, 0, ch * (line - last_row));
723           }
724      }
725 
726    code = pd->code;
727    line_obj = elm_code_file_line_get(code->file, line);
728    position = elm_code_widget_line_text_position_for_column_get(widget, line_obj, col);
729    text = elm_code_line_text_get(line_obj, &length);
730    if (position < length && text[position] == '\t')
731      pd->cursor_col = elm_code_widget_line_text_column_width_to_position(widget, line_obj, position);
732 
733    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CURSOR_CHANGED, widget);
734    if (!pd->selection || (pd->selection && pd->selection->in_progress))
735      _elm_code_widget_cursor_ensure_visible(widget);
736 
737    if (oldrow != pd->cursor_line)
738      _elm_code_widget_refresh(widget, line_obj);
739    else
740      _elm_code_widget_fill_line(widget, pd, elm_code_file_line_get(pd->code->file, pd->cursor_line));
741    if (pd->editable && pd->cursor_rect)
742      elm_layout_signal_emit(pd->cursor_rect, "elm,action,show,cursor", "elm");
743 }
744 
745 EOLIAN static Eina_Bool
_elm_code_widget_position_at_coordinates_get(Eo * obj,Elm_Code_Widget_Data * pd,Evas_Coord x,Evas_Coord y,unsigned int * row,int * col)746 _elm_code_widget_position_at_coordinates_get(Eo *obj, Elm_Code_Widget_Data *pd,
747                                              Evas_Coord x, Evas_Coord y,
748                                              unsigned int *row, int *col)
749 {
750    Elm_Code_Widget *widget;
751    Eina_List *item;
752    Elm_Code_Line *line;
753    Evas_Coord ox = 0, oy = 0, sx = 0, sy = 0, rowy = 0;
754    Evas_Object *grid;
755    int cw = 0, ch = 0, gutter, retcol;
756    unsigned int guess = 1, number = 1;
757 
758    widget = (Elm_Code_Widget *)obj;
759    evas_object_geometry_get(widget, &ox, &oy, NULL, NULL);
760    elm_scroller_region_get(pd->scroller, &sx, &sy, NULL, NULL);
761    x = x + sx - ox;
762    y = y + sy - oy;
763 
764    _elm_code_widget_cell_size_get(widget, &cw, &ch);
765    gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
766 
767    if (y >= 0 && ch > 0)
768      guess = ((double) y / ch) + 1;
769    if (guess > 1)
770      {
771         number = guess;
772 
773         // unfortunately EINA_LIST_REVERSE_FOREACH skips to the end of the list...
774         for (item = eina_list_nth_list(pd->grids, number - 1), grid = eina_list_data_get(item);
775              number > 1 && item;
776              item = eina_list_prev(item), grid = eina_list_data_get(item))
777           {
778              evas_object_geometry_get(grid, NULL, &rowy, NULL, NULL);
779 
780              if (rowy + sy - oy - 1 <= y)
781                break;
782 
783              number--;
784           }
785      }
786 
787    if (col)
788      {
789         if (cw == 0)
790            retcol = 1;
791         else
792            retcol = ((double) x / cw) - gutter + 1;
793 
794         if (retcol <= 0)
795            *col = 1;
796         else
797            *col = retcol;
798      }
799    if (row)
800      *row = number;
801 
802    line = elm_code_file_line_get(pd->code->file, number);
803    return !!line;
804 }
805 
806 EOLIAN static Eina_Bool
_elm_code_widget_geometry_for_position_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int row,int col,Evas_Coord * x,Evas_Coord * y,Evas_Coord * w,Evas_Coord * h)807 _elm_code_widget_geometry_for_position_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd, unsigned int row, int col,
808                                            Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
809 {
810    Elm_Code_Line *line;
811    Evas_Object *grid;
812    Evas_Coord cellw = 0;
813    unsigned int length = 0;
814    int gutter;
815 
816    line = elm_code_file_line_get(pd->code->file, row);
817    if (!line)
818      return EINA_FALSE;
819 
820    elm_code_line_text_get(line, &length);
821    _elm_code_widget_cell_size_get(widget, &cellw, h);
822    gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
823 
824    grid = eina_list_nth(pd->grids, row - 1);
825    evas_object_geometry_get(grid, x, y, NULL, NULL);
826 
827    if (x)
828      *x += (col - 1 + gutter) * cellw;
829    if (w)
830      *w = cellw;
831 
832    return !!line && col <= (int) length;
833 }
834 
835 EOLIAN static void
_elm_code_widget_line_status_toggle(Elm_Code_Widget * widget EINA_UNUSED,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)836 _elm_code_widget_line_status_toggle(Elm_Code_Widget *widget EINA_UNUSED, Elm_Code_Widget_Data *pd,
837                                Elm_Code_Line *line)
838 {
839    Evas_Object *status, *grid;
840    const char *template = "<color=#8B8B8B>%s</color>";
841    char *text;
842 
843    // add a status below the line if needed (and remove those no longer needed)
844    grid = eina_list_nth(pd->grids, line->number - 1);
845    status = evas_object_data_get(grid, "status");
846 
847    if (status)
848      {
849         elm_box_unpack(pd->gridbox, status);
850         evas_object_hide(status);
851         evas_object_data_set(grid, "status", NULL);
852      }
853    else
854      {
855         status = elm_label_add(pd->gridbox);
856         evas_object_size_hint_weight_set(status, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
857         evas_object_size_hint_align_set(status, 0.05, EVAS_HINT_FILL);
858         evas_object_show(status);
859 
860         elm_box_pack_after(pd->gridbox, status, grid);
861         evas_object_data_set(grid, "status", status);
862 
863         if (line->status_text)
864           {
865              text = malloc((strlen(template) + strlen(line->status_text) + 1) * sizeof(char));
866              sprintf(text, template, line->status_text);
867              elm_object_text_set(status, text);
868              free(text);
869           }
870      }
871 }
872 
873 static void
_popup_menu_dismissed_cb(void * data,const Efl_Event * event EINA_UNUSED)874 _popup_menu_dismissed_cb(void *data, const Efl_Event *event EINA_UNUSED)
875 {
876    Elm_Code_Widget *widget;
877    Elm_Code_Widget_Data *pd;
878 
879    widget = (Elm_Code_Widget *)data;
880    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
881 
882    if (pd->hoversel) evas_object_hide(pd->hoversel);
883 }
884 
885 static void
_popup_menu_cut_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)886 _popup_menu_cut_cb(void *data,
887         Evas_Object *obj EINA_UNUSED,
888         void *event_info EINA_UNUSED)
889 {
890    Elm_Code_Widget *widget;
891    Elm_Code_Widget_Data *pd;
892 
893    widget = (Elm_Code_Widget *)data;
894    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
895 
896    elm_code_widget_selection_cut(widget);
897    if (pd->hoversel) evas_object_hide(pd->hoversel);
898 }
899 
900 static void
_popup_menu_copy_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)901 _popup_menu_copy_cb(void *data,
902          Evas_Object *obj EINA_UNUSED,
903          void *event_info EINA_UNUSED)
904 {
905    Elm_Code_Widget *widget;
906    Elm_Code_Widget_Data *pd;
907 
908    widget = (Elm_Code_Widget *)data;
909    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
910 
911    elm_code_widget_selection_copy(widget);
912    if (pd->hoversel) evas_object_hide(pd->hoversel);
913 }
914 
915 static void
_popup_menu_paste_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)916 _popup_menu_paste_cb(void *data,
917           Evas_Object *obj EINA_UNUSED,
918           void *event_info EINA_UNUSED)
919 {
920    Elm_Code_Widget *widget;
921    Elm_Code_Widget_Data *pd;
922 
923    widget = (Elm_Code_Widget *)data;
924    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
925 
926    elm_code_widget_selection_paste(widget);
927    if (pd->hoversel) evas_object_hide(pd->hoversel);
928 }
929 
930 static void
_popup_menu_cancel_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)931 _popup_menu_cancel_cb(void *data,
932                  Evas_Object *obj EINA_UNUSED,
933                  void *event_info EINA_UNUSED)
934 {
935    Elm_Code_Widget *widget;
936    Elm_Code_Widget_Data *pd;
937 
938    widget = (Elm_Code_Widget *)data;
939    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
940 
941    elm_code_widget_selection_clear(widget);
942    if (pd->hoversel) evas_object_hide(pd->hoversel);
943 }
944 
945 static void
_popup_menu_show(Evas_Object * obj,Evas_Coord x,Evas_Coord y)946 _popup_menu_show(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
947 {
948    Elm_Code_Widget *widget;
949    Elm_Code_Widget_Data *pd;
950    Evas_Object *top;
951 
952    widget = (Elm_Code_Widget *)obj;
953    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
954 
955    if (pd->hoversel) evas_object_del(pd->hoversel);
956 
957    pd->hoversel = elm_hoversel_add(obj);
958    elm_object_tree_focus_allow_set(pd->hoversel, EINA_FALSE);
959    elm_widget_sub_object_add(obj, pd->hoversel);
960    top = elm_widget_top_get(obj);
961 
962    if (top) elm_hoversel_hover_parent_set(pd->hoversel, top);
963 
964    efl_event_callback_add
965      (pd->hoversel, ELM_HOVERSEL_EVENT_DISMISSED, _popup_menu_dismissed_cb, obj);
966    if (pd->selection)
967      {
968         if (pd->editable)
969           {
970              elm_hoversel_item_add
971                 (pd->hoversel, "Cut", NULL, ELM_ICON_NONE,
972                  _popup_menu_cut_cb, obj);
973           }
974         elm_hoversel_item_add
975            (pd->hoversel, "Copy", NULL, ELM_ICON_NONE,
976             _popup_menu_copy_cb, obj);
977         if (pd->editable)
978           {
979              elm_hoversel_item_add
980                 (pd->hoversel, "Paste", NULL, ELM_ICON_NONE,
981                  _popup_menu_paste_cb, obj);
982           }
983         elm_hoversel_item_add
984           (pd->hoversel, "Cancel", NULL, ELM_ICON_NONE,
985            _popup_menu_cancel_cb, obj);
986      }
987    else
988      {
989         if (pd->editable)
990           {
991              elm_hoversel_item_add
992                (pd->hoversel, "Paste", NULL, ELM_ICON_NONE,
993                 _popup_menu_paste_cb, obj);
994 
995           }
996         else
997           elm_hoversel_item_add
998              (pd->hoversel, "Cancel", NULL, ELM_ICON_NONE,
999               _popup_menu_cancel_cb, obj);
1000      }
1001 
1002    if (pd->hoversel)
1003      {
1004         evas_object_move(pd->hoversel, x, y);
1005         evas_object_show(pd->hoversel);
1006         elm_hoversel_hover_begin(pd->hoversel);
1007      }
1008 }
1009 
1010 static void
_elm_code_widget_clicked_gutter_cb(Elm_Code_Widget * widget,unsigned int row)1011 _elm_code_widget_clicked_gutter_cb(Elm_Code_Widget *widget, unsigned int row)
1012 {
1013    Elm_Code_Widget_Data *pd;
1014    Elm_Code_Line *line;
1015 
1016    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1017 
1018    line = elm_code_file_line_get(pd->code->file, row);
1019    if (!line)
1020      return;
1021 
1022    if (line->status_text)
1023      {
1024        elm_code_widget_line_status_toggle(widget, line);
1025        return;
1026      }
1027 
1028    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_LINE_GUTTER_CLICKED, line);
1029 }
1030 
1031 static void
_elm_code_widget_clicked_editable_cb(Elm_Code_Widget * widget,unsigned int row,unsigned int col)1032 _elm_code_widget_clicked_editable_cb(Elm_Code_Widget *widget, unsigned int row, unsigned int col)
1033 {
1034    Elm_Code_Widget_Data *pd;
1035    Elm_Code_Line *line;
1036    unsigned int column_width;
1037 
1038    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1039 
1040    line = elm_code_file_line_get(pd->code->file, row);
1041    if (!line)
1042      return;
1043    column_width = elm_code_widget_line_text_column_width_get(widget, line);
1044 
1045    if (col > column_width + 1)
1046      col = column_width + 1;
1047    else if (col <= 0)
1048      col = 1;
1049 
1050    _elm_code_widget_cursor_move(widget, pd, col, row);
1051 }
1052 
1053 static void
_elm_code_widget_clicked_readonly_cb(Elm_Code_Widget * widget,unsigned int row)1054 _elm_code_widget_clicked_readonly_cb(Elm_Code_Widget *widget, unsigned int row)
1055 {
1056    Elm_Code_Widget_Data *pd;
1057    Elm_Code_Line *line;
1058 
1059    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1060    line = elm_code_file_line_get(pd->code->file, row);
1061    if (!line)
1062      return;
1063 
1064    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_LINE_CLICKED, line);
1065 }
1066 
1067 static void
_mouse_selection_paste_at_position(Elm_Code_Widget * widget,unsigned int row,unsigned int col)1068 _mouse_selection_paste_at_position(Elm_Code_Widget *widget,
1069                                    unsigned int row, unsigned int col)
1070 {
1071    char *text;
1072 
1073    if (elm_code_widget_selection_is_empty(widget))
1074      return;
1075 
1076    text = elm_code_widget_selection_text_get(widget);
1077    elm_code_widget_selection_clear(widget);
1078 
1079    elm_code_widget_cursor_position_set(widget, row, col);
1080    elm_code_widget_text_at_cursor_insert(widget, text);
1081 
1082    free(text);
1083 }
1084 
1085 static Evas_Object *
_elm_code_widget_tooltip_cb(void * data,Evas_Object * obj EINA_UNUSED,Evas_Object * tooltip)1086 _elm_code_widget_tooltip_cb(void *data, Evas_Object *obj EINA_UNUSED, Evas_Object *tooltip)
1087 {
1088    Evas_Object *label;
1089 
1090    if (!data)
1091      return NULL;
1092 
1093    label = elm_label_add(tooltip);
1094    elm_object_text_set(label, (Eina_Stringshare *)data);
1095    return label;
1096 }
1097 
1098 static void
_elm_code_widget_tooltip_del_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)1099 _elm_code_widget_tooltip_del_cb(void *data, Evas_Object *obj EINA_UNUSED,
1100                                 void *event_info EINA_UNUSED)
1101 {
1102    if (!data)
1103      return;
1104 
1105    eina_stringshare_del((Eina_Stringshare *)data);
1106 }
1107 
1108 static void
_elm_code_widget_mouse_down_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)1109 _elm_code_widget_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1110                                void *event_info)
1111 {
1112    Elm_Code_Widget *widget;
1113    Elm_Code_Widget_Data *pd;
1114    Evas_Event_Mouse_Down *event;
1115    Eina_Bool ctrl, shift;
1116    unsigned int row;
1117    int col;
1118 
1119    widget = (Elm_Code_Widget *)data;
1120    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1121    event = (Evas_Event_Mouse_Down *)event_info;
1122    _elm_code_widget_position_at_coordinates_get(widget, pd, event->canvas.x, event->canvas.y, &row, &col);
1123 
1124    ctrl = evas_key_modifier_is_set(event->modifiers, "Control");
1125    shift = evas_key_modifier_is_set(event->modifiers, "Shift");
1126    if (!ctrl)
1127      {
1128         if (event->button == 3)
1129           {
1130              _popup_menu_show(widget, event->canvas.x, event->canvas.y);
1131              return;
1132           }
1133         else if (event->button == 2)
1134           {
1135              _mouse_selection_paste_at_position(widget, row, col);
1136              return;
1137           }
1138      }
1139 
1140    if (!shift)
1141      elm_code_widget_selection_clear(widget);
1142 
1143    if (event->flags & EVAS_BUTTON_TRIPLE_CLICK)
1144      {
1145         elm_code_widget_selection_select_line(widget, row);
1146         return;
1147      }
1148    else if (event->flags & EVAS_BUTTON_DOUBLE_CLICK)
1149      {
1150         elm_code_widget_selection_select_word(widget, row, col);
1151         return;
1152      }
1153 
1154    if (pd->editable)
1155      {
1156         if (shift && !pd->selection)
1157           elm_code_widget_selection_start(widget, pd->cursor_line, pd->cursor_col);
1158         _elm_code_widget_clicked_editable_cb(widget, row, (unsigned int) col);
1159         if (shift)
1160           elm_code_widget_selection_end(widget, pd->cursor_line, pd->cursor_col);
1161      }
1162 }
1163 
1164 static void
_elm_code_widget_mouse_move_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)1165 _elm_code_widget_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1166                             void *event_info)
1167 {
1168    Elm_Code_Widget *widget;
1169    Elm_Code_Widget_Data *pd;
1170    Evas_Event_Mouse_Move *event;
1171    unsigned int row;
1172    int col;
1173 
1174    widget = (Elm_Code_Widget *)data;
1175    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1176    event = (Evas_Event_Mouse_Move *)event_info;
1177 
1178    _elm_code_widget_position_at_coordinates_get(widget, pd, event->cur.canvas.x, event->cur.canvas.y, &row, &col);
1179 
1180    if (!pd->editable || !event->buttons)
1181      {
1182         Elm_Code_Line *line;
1183         Eina_Stringshare *text = NULL;
1184 
1185         line = elm_code_file_line_get(elm_code_widget_code_get(widget)->file, row);
1186         if (line && line->status_text)
1187           text = eina_stringshare_add(line->status_text);
1188         elm_object_tooltip_content_cb_set(pd->gridbox, _elm_code_widget_tooltip_cb,
1189                                           text, _elm_code_widget_tooltip_del_cb);
1190 
1191         return;
1192      }
1193 
1194    if (!pd->selection)
1195      if (col > 0 && row <= elm_code_file_lines_get(pd->code->file))
1196        elm_code_widget_selection_start(widget, row, col);
1197 
1198    _elm_code_widget_selection_type_set(widget, ELM_CODE_WIDGET_SELECTION_MOUSE);
1199    _elm_code_widget_selection_in_progress_set(widget, EINA_TRUE);
1200 
1201    elm_code_widget_selection_end(widget, row, col);
1202 }
1203 
1204 static void
_elm_code_widget_mouse_up_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)1205 _elm_code_widget_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1206                             void *event_info)
1207 {
1208    Elm_Code_Widget *widget;
1209    Elm_Code_Widget_Data *pd;
1210    Evas_Event_Mouse_Up *event;
1211    Evas_Coord x, y;
1212    unsigned int row;
1213    int col;
1214    Eina_Bool hasline;
1215 
1216    widget = (Elm_Code_Widget *)data;
1217    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1218    event = (Evas_Event_Mouse_Up *)event_info;
1219 
1220    if (pd->selection)
1221      {
1222         _elm_code_widget_selection_in_progress_set(widget, EINA_FALSE);
1223 
1224         if (pd->selection->start_line == pd->selection->end_line &&
1225             pd->selection->start_col == pd->selection->end_col)
1226           elm_code_widget_selection_clear(widget);
1227         else
1228           return;
1229      }
1230 
1231    x = event->canvas.x;
1232    y = event->canvas.y;
1233    hasline = _elm_code_widget_position_at_coordinates_get(widget, pd, x, y, &row, &col);
1234    if (!hasline)
1235      return;
1236 
1237    if (col <= 0)
1238      _elm_code_widget_clicked_gutter_cb(widget, row);
1239    else if (pd->editable)
1240      _elm_code_widget_clicked_editable_cb(widget, row, (unsigned int) col);
1241    else
1242      _elm_code_widget_clicked_readonly_cb(widget, row);
1243 }
1244 
1245 static void
_elm_code_widget_scroller_clicked_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)1246 _elm_code_widget_scroller_clicked_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1247                                      void *event_info)
1248 {
1249    Elm_Code_Widget *widget;
1250    Elm_Code_Widget_Data *pd;
1251    Evas_Event_Mouse_Down *event;
1252 
1253    widget = (Elm_Code_Widget *)data;
1254    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1255    event = (Evas_Event_Mouse_Down *)event_info;
1256 
1257    if (_elm_code_widget_position_at_coordinates_get(widget, pd,
1258          event->canvas.x, event->canvas.y, NULL, NULL))
1259      return;
1260 
1261    elm_code_widget_selection_clear(widget);
1262 }
1263 
1264 static void
_elm_code_widget_cursor_move_home(Elm_Code_Widget * widget)1265 _elm_code_widget_cursor_move_home(Elm_Code_Widget *widget)
1266 {
1267    Elm_Code_Widget_Data *pd;
1268 
1269    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1270 
1271    if (pd->cursor_col <= 1)
1272      return;
1273 
1274    _elm_code_widget_cursor_move(widget, pd, 1, pd->cursor_line);
1275 }
1276 
1277 static void
_elm_code_widget_cursor_move_end(Elm_Code_Widget * widget)1278 _elm_code_widget_cursor_move_end(Elm_Code_Widget *widget)
1279 {
1280    Elm_Code_Widget_Data *pd;
1281    Elm_Code_Line *line;
1282    unsigned int lastcol;
1283 
1284    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1285 
1286    line = elm_code_file_line_get(pd->code->file, pd->cursor_line);
1287    lastcol = elm_code_widget_line_text_column_width_get(widget, line);
1288    if (pd->cursor_col > lastcol + 1)
1289      return;
1290 
1291    _elm_code_widget_cursor_move(widget, pd, lastcol + 1, pd->cursor_line);
1292 }
1293 
1294 static void
_elm_code_widget_cursor_move_up(Elm_Code_Widget * widget)1295 _elm_code_widget_cursor_move_up(Elm_Code_Widget *widget)
1296 {
1297    Elm_Code_Widget_Data *pd;
1298    Elm_Code_Line *line;
1299    unsigned int row, col, column_width;
1300 
1301    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1302    row = pd->cursor_line;
1303    col = pd->cursor_col;
1304 
1305    if (pd->cursor_line <= 1)
1306      return;
1307 
1308    row--;
1309    line = elm_code_file_line_get(pd->code->file, row);
1310    column_width = elm_code_widget_line_text_column_width_get(widget, line);
1311    if (col > column_width + 1)
1312      col = column_width + 1;
1313 
1314    _elm_code_widget_cursor_move(widget, pd, col, row);
1315 }
1316 
1317 static void
_elm_code_widget_cursor_move_down(Elm_Code_Widget * widget)1318 _elm_code_widget_cursor_move_down(Elm_Code_Widget *widget)
1319 {
1320    Elm_Code_Widget_Data *pd;
1321    Elm_Code_Line *line;
1322    unsigned int row, col, column_width;
1323 
1324    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1325    row = pd->cursor_line;
1326    col = pd->cursor_col;
1327 
1328    if (pd->cursor_line >= elm_code_file_lines_get(pd->code->file))
1329      return;
1330 
1331    row++;
1332    line = elm_code_file_line_get(pd->code->file, row);
1333    column_width = elm_code_widget_line_text_column_width_get(widget, line);
1334    if (col > column_width + 1)
1335      col = column_width + 1;
1336 
1337    _elm_code_widget_cursor_move(widget, pd, col, row);
1338 }
1339 
1340 static void
_elm_code_widget_cursor_move_left(Elm_Code_Widget * widget)1341 _elm_code_widget_cursor_move_left(Elm_Code_Widget *widget)
1342 {
1343    Elm_Code_Widget_Data *pd;
1344 
1345    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1346 
1347    if (pd->cursor_col <= 1)
1348      {
1349         if (pd->cursor_line > 1)
1350           {
1351              _elm_code_widget_cursor_move_up(widget);
1352              _elm_code_widget_cursor_move_end(widget);
1353           }
1354         return;
1355      }
1356 
1357    _elm_code_widget_cursor_move(widget, pd, pd->cursor_col-1, pd->cursor_line);
1358 }
1359 
1360 static void
_elm_code_widget_cursor_move_right(Elm_Code_Widget * widget)1361 _elm_code_widget_cursor_move_right(Elm_Code_Widget *widget)
1362 {
1363    Elm_Code_Widget_Data *pd;
1364    Elm_Code_Line *line;
1365    unsigned int position, next_col;
1366 
1367    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1368 
1369    line = elm_code_file_line_get(pd->code->file, pd->cursor_line);
1370    if (pd->cursor_col > elm_code_widget_line_text_column_width_get(widget, line))
1371      {
1372         if (pd->cursor_line < elm_code_file_lines_get(pd->code->file))
1373           {
1374              _elm_code_widget_cursor_move_down(widget);
1375              _elm_code_widget_cursor_move_home(widget);
1376           }
1377         return;
1378      }
1379 
1380    next_col = pd->cursor_col + 1;
1381    position = elm_code_widget_line_text_position_for_column_get(widget, line, pd->cursor_col);
1382    if (elm_code_line_text_get(line, NULL)[position] == '\t')
1383      next_col = pd->cursor_col + pd->tabstop;
1384 
1385    _elm_code_widget_cursor_move(widget, pd, next_col, pd->cursor_line);
1386 }
1387 
1388 static unsigned int
_elm_code_widget_cursor_move_page_height_get(Elm_Code_Widget * widget)1389 _elm_code_widget_cursor_move_page_height_get(Elm_Code_Widget *widget)
1390 {
1391    unsigned int lines;
1392 
1393    lines = efl_ui_code_widget_lines_visible_get(widget);
1394    return lines * 0.85;
1395 }
1396 
1397 static void
_elm_code_widget_cursor_move_pageup(Elm_Code_Widget * widget)1398 _elm_code_widget_cursor_move_pageup(Elm_Code_Widget *widget)
1399 {
1400    Elm_Code_Widget_Data *pd;
1401    Elm_Code_Line *line;
1402    unsigned int row, col, column_width;
1403 
1404    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1405    row = pd->cursor_line;
1406    col = pd->cursor_col;
1407 
1408    if (pd->cursor_line <= 1)
1409      return;
1410 
1411    if (row > _elm_code_widget_cursor_move_page_height_get(widget))
1412      row -= _elm_code_widget_cursor_move_page_height_get(widget);
1413    else
1414      row = 1;
1415 
1416    line = elm_code_file_line_get(pd->code->file, row);
1417    column_width = elm_code_widget_line_text_column_width_get(widget, line);
1418    if (col > column_width + 1)
1419      col = column_width + 1;
1420 
1421    _elm_code_widget_cursor_move(widget, pd, col, row);
1422 }
1423 
1424 static void
_elm_code_widget_cursor_move_pagedown(Elm_Code_Widget * widget)1425 _elm_code_widget_cursor_move_pagedown(Elm_Code_Widget *widget)
1426 {
1427    Elm_Code_Widget_Data *pd;
1428    Elm_Code_Line *line;
1429    unsigned int row, col, column_width;
1430 
1431    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1432    row = pd->cursor_line;
1433    col = pd->cursor_col;
1434 
1435    if (pd->cursor_line >= elm_code_file_lines_get(pd->code->file))
1436      return;
1437 
1438    row += _elm_code_widget_cursor_move_page_height_get(widget);
1439    if (row > elm_code_file_lines_get(pd->code->file))
1440      row = elm_code_file_lines_get(pd->code->file);
1441 
1442    line = elm_code_file_line_get(pd->code->file, row);
1443    column_width = elm_code_widget_line_text_column_width_get(widget, line);
1444    if (col > column_width + 1)
1445      col = column_width + 1;
1446 
1447    _elm_code_widget_cursor_move(widget, pd, col, row);
1448 }
1449 
1450 static Elm_Code_Widget_Change_Info *
_elm_code_widget_change_create(unsigned int start_col,unsigned int start_line,unsigned int end_col,unsigned int end_line,const char * text,unsigned int length,Eina_Bool insert)1451 _elm_code_widget_change_create(unsigned int start_col, unsigned int start_line,
1452                                unsigned int end_col, unsigned int end_line,
1453                                const char *text, unsigned int length, Eina_Bool insert)
1454 {
1455    Elm_Code_Widget_Change_Info *info;
1456 
1457    info = calloc(1, sizeof(*info));
1458    if (!info) return NULL;
1459    info->insert = insert;
1460 
1461    info->start_col = start_col;
1462    info->start_line = start_line;
1463    info->end_col = end_col;
1464    info->end_line = end_line;
1465 
1466    info->content = eina_strndup(text, length);
1467    info->length = length;
1468 
1469    return info;
1470 }
1471 
1472 static void
_elm_code_widget_change_free(Elm_Code_Widget_Change_Info * info)1473 _elm_code_widget_change_free(Elm_Code_Widget_Change_Info *info)
1474 {
1475    free((char *)info->content);
1476    free(info);
1477 }
1478 
1479 void
_elm_code_widget_change_selection_add(Evas_Object * widget)1480 _elm_code_widget_change_selection_add(Evas_Object *widget)
1481 {
1482    Elm_Code_Widget_Change_Info *change;
1483    Elm_Code_Widget_Selection_Data *selection;
1484    char *selection_text;
1485 
1486    if (elm_code_widget_selection_is_empty(widget))
1487      return;
1488 
1489    selection_text = elm_code_widget_selection_text_get(widget);
1490    selection = elm_code_widget_selection_normalized_get(widget);
1491 
1492    change = _elm_code_widget_change_create(selection->start_col,
1493                                            selection->start_line,
1494                                            selection->end_col,
1495                                            selection->end_line,
1496                                            selection_text,
1497                                            strlen(selection_text),
1498                                            EINA_FALSE);
1499    _elm_code_widget_undo_change_add(widget, change);
1500    _elm_code_widget_change_free(change);
1501    free(selection_text);
1502    free(selection);
1503 }
1504 
1505 static void
_elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget * widget)1506 _elm_code_widget_tab_at_cursor_insert(Elm_Code_Widget *widget)
1507 {
1508    Elm_Code_Widget_Data *pd;
1509    unsigned int col, row, rem;
1510 
1511    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1512    if (!pd->tab_inserts_spaces)
1513      {
1514         elm_code_widget_text_at_cursor_insert(widget, "\t");
1515         return;
1516      }
1517 
1518    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
1519    rem = (col - 1) % pd->tabstop;
1520 
1521    while (rem < pd->tabstop)
1522      {
1523         elm_code_widget_text_at_cursor_insert(widget, " ");
1524         rem++;
1525      }
1526 }
1527 
1528 static void
_elm_code_widget_scroll_newline(Elm_Code_Widget * widget)1529 _elm_code_widget_scroll_newline(Elm_Code_Widget *widget)
1530 {
1531    Elm_Code_Widget_Data *pd;
1532    Evas_Coord x, y, w, h;
1533 
1534    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1535 
1536    elm_scroller_region_get(pd->scroller, &x, &y, &w, &h);
1537    elm_scroller_region_show(pd->scroller, 0, y, w, h);
1538 }
1539 
1540 void
_elm_code_widget_newline(Elm_Code_Widget * widget)1541 _elm_code_widget_newline(Elm_Code_Widget *widget)
1542 {
1543    Elm_Code *code;
1544    Elm_Code_Line *line;
1545    Elm_Code_Widget_Change_Info *change;
1546    unsigned int row, col, position, oldlen, width, indent, textlen;
1547    char *oldtext, *leading, *text;
1548 
1549    if (!elm_code_widget_selection_is_empty(widget))
1550      elm_code_widget_selection_delete(widget);
1551 
1552    code = efl_ui_code_widget_code_get(widget);
1553    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
1554    line = elm_code_file_line_get(code->file, row);
1555    if (line == NULL)
1556      {
1557         elm_code_file_line_append(code->file, "", 0, NULL);
1558         row = elm_code_file_lines_get(code->file);
1559         line = elm_code_file_line_get(code->file, row);
1560      }
1561    oldtext = (char *) elm_code_line_text_get(line, &oldlen);
1562    oldtext = eina_strndup(oldtext, oldlen);
1563 
1564    position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
1565    elm_code_line_split_at(line, position);
1566    width = elm_code_widget_line_text_column_width_get(widget, line);
1567 
1568    line = elm_code_file_line_get(code->file, row + 1);
1569    leading = elm_code_line_indent_get(line);
1570    elm_code_line_text_leading_whitespace_strip(line);
1571    elm_code_widget_cursor_position_set(widget, row + 1, 1);
1572    elm_code_widget_text_at_cursor_insert(widget, leading);
1573    free(oldtext);
1574 
1575    indent = efl_ui_code_widget_line_text_column_width_to_position(widget, line,
1576      strlen(leading));
1577    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
1578 
1579    textlen = strlen(leading) + 2;
1580    text = malloc(sizeof(char) * textlen);
1581    if (!text) return;
1582    snprintf(text, textlen, "\n%s", leading);
1583    free(leading);
1584 
1585    change = _elm_code_widget_change_create(width + 1, row, indent - 1, row + 1, text, strlen(text), EINA_TRUE);
1586    _elm_code_widget_undo_change_add(widget, change);
1587    _elm_code_widget_change_free(change);
1588    free(text);
1589 
1590    _elm_code_widget_scroll_newline(widget);
1591 }
1592 
1593 static void
_elm_code_widget_backspaceline(Elm_Code_Widget * widget,Eina_Bool nextline)1594 _elm_code_widget_backspaceline(Elm_Code_Widget *widget, Eina_Bool nextline)
1595 {
1596    Elm_Code *code;
1597    Elm_Code_Line *line, *oldline;
1598    Eina_Bool cursor_move = EINA_TRUE;
1599    unsigned int row, col, oldlength, position = 0;
1600 
1601    code = efl_ui_code_widget_code_get(widget);
1602    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
1603    line = elm_code_file_line_get(code->file, row);
1604 
1605    if (nextline)
1606      {
1607         elm_code_widget_selection_start(widget, row, col);
1608         elm_code_widget_selection_end(widget, row + 1, 0);
1609         _elm_code_widget_change_selection_add(widget);
1610 
1611         if (col >= line->length)
1612           cursor_move = EINA_FALSE;
1613 
1614         elm_code_line_merge_down(line);
1615      }
1616    else
1617      {
1618         oldline = elm_code_file_line_get(code->file, row - 1);
1619         elm_code_line_text_get(oldline, &oldlength);
1620 
1621         position = elm_code_widget_line_text_column_width_to_position(widget, oldline, oldlength);
1622         elm_code_widget_selection_start(widget, row - 1, position);
1623         elm_code_widget_selection_end(widget, row, 0);
1624         _elm_code_widget_change_selection_add(widget);
1625 
1626         elm_code_line_merge_up(line);
1627      }
1628 
1629    elm_code_widget_selection_clear(widget);
1630 
1631    line = elm_code_file_line_get(code->file, row - 1);
1632    if (line && cursor_move)
1633      {
1634         if (position)
1635           elm_code_widget_cursor_position_set(widget, row - 1, position);
1636         else
1637           elm_code_widget_cursor_position_set(widget, row - 1, line->length + 1);
1638      }
1639 
1640    // TODO construct and pass a change object
1641    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
1642 }
1643 
1644 void
_elm_code_widget_backspace(Elm_Code_Widget * widget)1645 _elm_code_widget_backspace(Elm_Code_Widget *widget)
1646 {
1647    Elm_Code *code;
1648    Elm_Code_Line *line;
1649    Elm_Code_Widget_Change_Info *change;
1650    unsigned int row, col, position, start_col, end_col, char_width;
1651    const char *text;
1652 
1653    if (!elm_code_widget_selection_is_empty(widget))
1654      {
1655         elm_code_widget_selection_delete(widget);
1656         return;
1657      }
1658 
1659    code = efl_ui_code_widget_code_get(widget);
1660    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
1661 
1662    if (col <= 1)
1663      {
1664         if (row == 1)
1665           return;
1666 
1667         _elm_code_widget_backspaceline(widget, EINA_FALSE);
1668         return;
1669      }
1670 
1671    line = elm_code_file_line_get(code->file, row);
1672 
1673    position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
1674    end_col = elm_code_widget_line_text_column_width_to_position(widget, line, position);
1675    start_col = elm_code_widget_line_text_column_width_to_position(widget, line,
1676       elm_code_widget_line_text_position_for_column_get(widget, line, col - 1));
1677    char_width = position - elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
1678 
1679    text = elm_code_widget_text_between_positions_get(widget, row, start_col, row, end_col);
1680    elm_code_line_text_remove(line, position - char_width, char_width);
1681    efl_ui_code_widget_cursor_position_set(widget, row, start_col);
1682 
1683    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
1684 
1685    change = _elm_code_widget_change_create(start_col, row, end_col - 1, row, text, char_width, EINA_FALSE);
1686    _elm_code_widget_undo_change_add(widget, change);
1687    _elm_code_widget_change_free(change);
1688 }
1689 
1690 void
_elm_code_widget_delete(Elm_Code_Widget * widget)1691 _elm_code_widget_delete(Elm_Code_Widget *widget)
1692 {
1693    Elm_Code *code;
1694    Elm_Code_Line *line;
1695    Elm_Code_Widget_Change_Info *change;
1696    unsigned int row, col, position, char_width, start_col, end_col;
1697    const char *text;
1698 
1699    if (!elm_code_widget_selection_is_empty(widget))
1700      {
1701         elm_code_widget_selection_delete(widget);
1702         return;
1703      }
1704 
1705    code = efl_ui_code_widget_code_get(widget);
1706    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
1707    line = elm_code_file_line_get(code->file, row);
1708    if (col > elm_code_widget_line_text_column_width_get(widget, line))
1709      {
1710         if (row == elm_code_file_lines_get(code->file))
1711           return;
1712 
1713         _elm_code_widget_backspaceline(widget, EINA_TRUE);
1714         return;
1715      }
1716 
1717    position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
1718    char_width = elm_code_widget_line_text_position_for_column_get(widget, line, col + 1) - position;
1719    if (char_width == 0) // a partial tab
1720      char_width = 1;
1721    start_col = elm_code_widget_line_text_column_width_to_position(widget, line, position);
1722    end_col = elm_code_widget_line_text_column_width_to_position(widget, line, position + char_width);
1723 
1724    text = elm_code_widget_text_between_positions_get(widget, row, start_col, row, end_col);
1725    elm_code_line_text_remove(line, position, char_width);
1726    efl_ui_code_widget_cursor_position_set(widget, row, start_col);
1727    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
1728 
1729    change = _elm_code_widget_change_create(start_col, row, col - 1, row, text, char_width, EINA_FALSE);
1730    _elm_code_widget_undo_change_add(widget, change);
1731    _elm_code_widget_change_free(change);
1732 }
1733 
1734 static void
_elm_code_widget_control_key_down_cb(Elm_Code_Widget * widget,const char * key)1735 _elm_code_widget_control_key_down_cb(Elm_Code_Widget *widget, const char *key)
1736 {
1737    if (!key)
1738      return;
1739 
1740    if (!strcmp("c", key))
1741      {
1742         elm_code_widget_selection_copy(widget);
1743         return;
1744      }
1745 
1746    if (!strcmp("v", key))
1747      elm_code_widget_selection_paste(widget);
1748    else if (!strcmp("x", key))
1749      elm_code_widget_selection_cut(widget);
1750    else if (!strcmp("y", key))
1751      elm_code_widget_redo(widget);
1752    else if (!strcmp("z", key))
1753      elm_code_widget_undo(widget);
1754    else if (!strcmp("a", key))
1755      elm_code_widget_selection_select_all(widget);
1756 }
1757 
1758 static Eina_Bool
_elm_code_widget_key_cursor_is(const char * key)1759 _elm_code_widget_key_cursor_is(const char *key)
1760 {
1761    if (!strcmp(key, "Up") || !strcmp(key, "Down"))
1762      return EINA_TRUE;
1763    if (!strcmp(key, "Left") || !strcmp(key, "Right"))
1764      return EINA_TRUE;
1765    if (!strcmp(key, "Home") || !strcmp(key, "End"))
1766      return EINA_TRUE;
1767    if (!strcmp(key, "Prior") || !strcmp(key, "Next"))
1768      return EINA_TRUE;
1769 
1770    return EINA_FALSE;
1771 }
1772 
1773 static void
_elm_code_widget_key_down_cb(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info)1774 _elm_code_widget_key_down_cb(void *data, Evas *evas EINA_UNUSED,
1775                               Evas_Object *obj EINA_UNUSED, void *event_info)
1776 {
1777    Elm_Code_Widget *widget;
1778    Elm_Code_Widget_Data *pd;
1779    Eina_Bool shift, adjust, backwards = EINA_FALSE;
1780 
1781    widget = (Elm_Code_Widget *)data;
1782    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1783 
1784    Evas_Event_Key_Down *ev = event_info;
1785 
1786    if (!pd->editable || (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD))
1787      return;
1788 
1789 #if defined(__APPLE__) && defined(__MACH__)
1790    if (evas_key_modifier_is_set(ev->modifiers, "Super"))
1791 #else
1792    if (evas_key_modifier_is_set(ev->modifiers, "Control"))
1793 #endif
1794      {
1795         _elm_code_widget_control_key_down_cb(widget, ev->key);
1796         return;
1797      }
1798 
1799    shift = evas_key_modifier_is_set(ev->modifiers, "Shift");
1800    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
1801 
1802    if (_elm_code_widget_key_cursor_is(ev->key))
1803      {
1804         if (shift)
1805           {
1806              backwards = !strcmp(ev->key, "Up") || !strcmp(ev->key, "Left") ||
1807                          !strcmp(ev->key, "Home") || !strcmp(ev->key, "Prior");
1808 
1809              if (!pd->selection)
1810                elm_code_widget_selection_start(widget, pd->cursor_line, pd->cursor_col - (backwards?1:0));
1811 
1812              _elm_code_widget_selection_type_set(widget, ELM_CODE_WIDGET_SELECTION_KEYBOARD);
1813              _elm_code_widget_selection_in_progress_set(widget, EINA_TRUE);
1814           }
1815         else
1816           elm_code_widget_selection_clear(widget);
1817 
1818         if (!strcmp(ev->key, "Up"))
1819           _elm_code_widget_cursor_move_up(widget);
1820         else if (!strcmp(ev->key, "Down"))
1821           _elm_code_widget_cursor_move_down(widget);
1822         else if (!strcmp(ev->key, "Left"))
1823           _elm_code_widget_cursor_move_left(widget);
1824         else if (!strcmp(ev->key, "Right"))
1825           _elm_code_widget_cursor_move_right(widget);
1826         else if (!strcmp(ev->key, "Home"))
1827           _elm_code_widget_cursor_move_home(widget);
1828         else if (!strcmp(ev->key, "End"))
1829           _elm_code_widget_cursor_move_end(widget);
1830         else if (!strcmp(ev->key, "Prior"))
1831           _elm_code_widget_cursor_move_pageup(widget);
1832         else if (!strcmp(ev->key, "Next"))
1833           _elm_code_widget_cursor_move_pagedown(widget);
1834 
1835         if (shift && pd->selection)
1836           {
1837              if (pd->selection->start_line == pd->selection->end_line)
1838                adjust = (pd->selection->end_col > pd->selection->start_col) ||
1839                          (!backwards && (pd->selection->end_col == pd->selection->start_col));
1840              else
1841                adjust = (pd->selection->end_line > pd->selection->start_line);
1842 
1843              elm_code_widget_selection_end(widget, pd->cursor_line, pd->cursor_col - (adjust?1:0));
1844              _elm_code_widget_selection_in_progress_set(widget, EINA_FALSE);
1845 
1846              if (pd->selection)
1847                {
1848                   if (pd->selection->end_line < pd->selection->start_line)
1849                     {
1850                        elm_code_widget_cursor_position_set(widget, pd->selection->end_line, pd->selection->end_col);
1851                     }
1852                   else if ((pd->selection->end_col == pd->selection->start_col && !backwards) ||
1853                            (pd->selection->end_col > pd->selection->start_col) ||
1854                            (pd->selection->end_line > pd->selection->start_line))
1855                     {
1856                        elm_code_widget_cursor_position_set(widget, pd->selection->end_line, pd->selection->end_col+1);
1857                     }
1858                }
1859           }
1860      }
1861 
1862    else if (!strcmp(ev->key, "KP_Enter") || !strcmp(ev->key, "Return"))
1863      _elm_code_widget_newline(widget);
1864    else if (!strcmp(ev->key, "BackSpace"))
1865      _elm_code_widget_backspace(widget);
1866    else if (!strcmp(ev->key, "Delete"))
1867      _elm_code_widget_delete(widget);
1868    else if (!strcmp(ev->key, "Tab"))
1869      _elm_code_widget_tab_at_cursor_insert(widget);
1870 
1871    else if (!strcmp(ev->key, "Escape"))
1872      elm_code_widget_selection_clear(widget);
1873 
1874    else if (ev->string)
1875      elm_code_widget_text_at_cursor_insert(widget, ev->string);
1876    else
1877      INF("Unhandled key %s (%s)", ev->key, ev->keyname);
1878 }
1879 
1880 static void
_elm_code_widget_focused_event_cb(void * data,Evas_Object * obj,void * event_info EINA_UNUSED)1881 _elm_code_widget_focused_event_cb(void *data, Evas_Object *obj,
1882                                   void *event_info EINA_UNUSED)
1883 {
1884    Elm_Code_Widget *widget;
1885    Elm_Code_Widget_Data *pd;
1886 
1887    widget = (Elm_Code_Widget *)data;
1888    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1889 
1890    pd->focused = EINA_TRUE;
1891    if (pd->cursor_rect)
1892      elm_layout_signal_emit(pd->cursor_rect, "elm,action,focus", "elm");
1893 
1894    _elm_code_widget_refresh(obj, NULL);
1895 }
1896 
1897 static void
_elm_code_widget_unfocused_event_cb(void * data,Evas_Object * obj,void * event_info EINA_UNUSED)1898 _elm_code_widget_unfocused_event_cb(void *data, Evas_Object *obj,
1899                                     void *event_info EINA_UNUSED)
1900 {
1901    Elm_Code_Widget *widget;
1902    Elm_Code_Widget_Data *pd;
1903 
1904    widget = (Elm_Code_Widget *)data;
1905    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
1906 
1907    pd->focused = EINA_FALSE;
1908    if (pd->cursor_rect)
1909      elm_layout_signal_emit(pd->cursor_rect, "elm,action,unfocus", "elm");
1910 
1911   _elm_code_widget_refresh(obj, NULL);
1912 }
1913 
1914 static void
_elm_code_widget_scroll_event_cb(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)1915 _elm_code_widget_scroll_event_cb(void *data, Evas_Object *obj EINA_UNUSED,
1916                                  void *event_info EINA_UNUSED)
1917 {
1918    Elm_Code_Widget *widget;
1919 
1920    widget = (Elm_Code_Widget *)data;
1921 
1922    _elm_code_widget_refresh(widget, NULL);
1923 }
1924 
1925 EOLIAN static Eina_Bool
_elm_code_widget_efl_ui_widget_widget_input_event_handler(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd EINA_UNUSED,const Efl_Event * eo_event,Evas_Object * src EINA_UNUSED)1926 _elm_code_widget_efl_ui_widget_widget_input_event_handler(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd EINA_UNUSED, const Efl_Event *eo_event, Evas_Object *src EINA_UNUSED)
1927 {
1928    Eo *ev = eo_event->info;
1929 
1930    if (efl_input_processed_get(ev)) return EINA_FALSE;
1931    if (eo_event->desc != EFL_EVENT_KEY_DOWN) return EINA_FALSE;
1932 
1933    // FIXME: This should use key bindings and the standard implementation!
1934    if (eina_streq(efl_input_key_sym_get(ev), "BackSpace"))
1935      {
1936         efl_input_processed_set(ev, EINA_TRUE);
1937         return EINA_TRUE;
1938      }
1939 
1940    return EINA_FALSE;
1941 }
1942 
1943 // load a named colour class from the theme and apply it to the grid's specified palette
1944 static void
_elm_code_widget_setup_palette_item(Evas_Object * grid,int type,const char * name,double fade,Evas_Object * edje)1945 _elm_code_widget_setup_palette_item(Evas_Object *grid, int type, const char *name,
1946                                     double fade, Evas_Object *edje)
1947 {
1948    int r, g, b, a;
1949 
1950    if (!edje_object_color_class_get(edje, name, &r, &g, &b, &a,
1951                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
1952      return;
1953 
1954    evas_object_textgrid_palette_set(grid, EVAS_TEXTGRID_PALETTE_STANDARD, type,
1955                                     r * fade, g * fade, b * fade, a * fade);
1956 }
1957 
1958 static void
_elm_code_widget_setup_palette(Evas_Object * o,Evas_Object * layout,float fade)1959 _elm_code_widget_setup_palette(Evas_Object *o, Evas_Object *layout, float fade)
1960 {
1961    Evas_Object *edje;
1962 
1963    edje = elm_layout_edje_get(layout);
1964 
1965    // setup status colors
1966    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_DEFAULT, "elm/code/status/default", fade, edje);
1967    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_CURRENT, "elm/code/status/current", fade, edje);
1968    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_IGNORED, "elm/code/status/ignored", fade, edje);
1969    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_NOTE, "elm/code/status/note", fade, edje);
1970    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_WARNING, "elm/code/status/warning", fade, edje);
1971    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_ERROR, "elm/code/status/error", fade, edje);
1972    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_FATAL, "elm/code/status/fatal", fade, edje);
1973    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_ADDED, "elm/code/status/added", fade, edje);
1974    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_REMOVED, "elm/code/status/removed", fade, edje);
1975    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_CHANGED, "elm/code/status/changed", fade, edje);
1976    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_PASSED, "elm/code/status/passed", fade, edje);
1977    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_FAILED, "elm/code/status/failed", fade, edje);
1978    _elm_code_widget_setup_palette_item(o, ELM_CODE_STATUS_TYPE_TODO, "elm/code/status/todo", fade, edje);
1979 
1980    // setup token colors
1981    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_DEFAULT, "elm/code/token/default", fade, edje);
1982    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_COMMENT, "elm/code/token/comment", fade, edje);
1983    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_STRING, "elm/code/token/string", fade, edje);
1984    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_NUMBER, "elm/code/token/number", fade, edje);
1985    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_BRACE, "elm/code/token/brace", fade, edje);
1986    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_TYPE, "elm/code/token/type", fade, edje);
1987    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_CLASS, "elm/code/token/class", fade, edje);
1988    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_FUNCTION, "elm/code/token/function", fade, edje);
1989    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_PARAM, "elm/code/token/param", fade, edje);
1990    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_KEYWORD, "elm/code/token/keyword", fade, edje);
1991    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_PREPROCESSOR, "elm/code/token/preprocessor", fade, edje);
1992    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_ADDED, "elm/code/token/added", fade, edje);
1993    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_REMOVED, "elm/code/token/removed", fade, edje);
1994    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_CHANGED, "elm/code/token/changed", fade, edje);
1995    _elm_code_widget_setup_palette_item(o, ELM_CODE_TOKEN_TYPE_MATCH, "elm/code/token/match", fade, edje);
1996 
1997    // other styles that the widget uses
1998    _elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_SELECTION, "elm/code/widget/color/selection", fade, edje);
1999    _elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_GUTTER_BG, "elm/code/widget/color/gutter/bg", fade, edje);
2000    _elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_GUTTER_FG, "elm/code/widget/color/gutter/fg", fade, edje);
2001    _elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_GUTTER_SCOPE_BG, "elm/code/widget/color/scope/bg", fade, edje);
2002    _elm_code_widget_setup_palette_item(o, ELM_CODE_WIDGET_COLOR_WHITESPACE, "elm/code/widget/color/whitespace", fade, edje);
2003 }
2004 
2005 static void
_elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget * widget,int rows)2006 _elm_code_widget_ensure_n_grid_rows(Elm_Code_Widget *widget, int rows)
2007 {
2008    Evas_Object *grid;
2009    int existing, i;
2010    Elm_Code_Widget_Data *pd;
2011 
2012    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
2013    existing = eina_list_count(pd->grids);
2014 
2015    // trim unneeded rows in our rendering
2016    if (rows < existing)
2017      {
2018         for (i = existing - rows; i > 0; i--)
2019           {
2020              grid = eina_list_data_get(eina_list_last(pd->grids));
2021              evas_object_del(grid);
2022              elm_box_unpack(pd->gridbox, grid);
2023              pd->grids = eina_list_remove_list(pd->grids, eina_list_last(pd->grids));
2024           }
2025         rows = existing;
2026      }
2027 
2028    if (rows == existing)
2029      return;
2030 
2031    for (i = existing; i < rows; i++)
2032      {
2033         grid = evas_object_textgrid_add(evas_object_evas_get(pd->gridbox));
2034         evas_object_size_hint_weight_set(grid, EVAS_HINT_EXPAND, 0.0);
2035         evas_object_size_hint_align_set(grid, EVAS_HINT_FILL, 0.0);
2036         evas_object_show(grid);
2037         _elm_code_widget_setup_palette(grid, widget, (double) pd->alpha / 255);
2038 
2039         elm_box_pack_end(pd->gridbox, grid);
2040         pd->grids = eina_list_append(pd->grids, grid);
2041 
2042         evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_DOWN, _elm_code_widget_mouse_down_cb, widget);
2043         evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_MOVE, _elm_code_widget_mouse_move_cb, widget);
2044         evas_object_event_callback_add(grid, EVAS_CALLBACK_MOUSE_UP, _elm_code_widget_mouse_up_cb, widget);
2045 
2046         evas_object_textgrid_font_set(grid, pd->font_name, pd->font_size * elm_config_scale_get());
2047      }
2048 }
2049 
2050 static void
_elm_code_widget_resize(Elm_Code_Widget * widget,Elm_Code_Line * newline)2051 _elm_code_widget_resize(Elm_Code_Widget *widget, Elm_Code_Line *newline)
2052 {
2053    Eina_List *item, *lines;
2054    Elm_Code_Widget_Data *pd;
2055    Elm_Code_Line *line;
2056    Evas_Object *grid;
2057    Evas_Coord ww, wh, old_width, old_height;
2058    int w = 0, h, cw = 0, ch = 0, gutter;
2059    unsigned int i, n, line_width, first_row = 1, last_row = 256;
2060    Eina_Bool viewport = EINA_FALSE;
2061 
2062    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
2063    gutter = efl_ui_code_widget_text_left_gutter_width_get(widget);
2064 
2065    if (!pd->code)
2066      return;
2067 
2068    if (!pd->code->file->lines)
2069      return;
2070 
2071    evas_object_geometry_get(widget, NULL, NULL, &ww, &wh);
2072 
2073    old_width = ww;
2074    old_height = wh;
2075 
2076    n = h = elm_code_file_lines_get(pd->code->file);
2077 
2078    if (_elm_code_widget_viewport_get(widget, pd, &first_row, &last_row))
2079      viewport = EINA_TRUE;
2080 
2081    /* Grow by one page at a time where possible. */
2082    n = (last_row + (last_row - first_row)) < n ?
2083         last_row + (last_row - first_row) : n;
2084 
2085    /* Calculate the maximum width of our lines. */
2086 
2087    lines = eina_list_nth_list(pd->code->file->lines, first_row - 1);
2088    for (i = 0; i < n; i++)
2089      {
2090         line = eina_list_data_get(lines);
2091         if (!line) break;
2092         line_width = elm_code_widget_line_text_column_width_get(widget, line);
2093 
2094         if ((int) line_width + gutter + 1 > w)
2095           w = (int) line_width + gutter + 1;
2096 
2097         lines = eina_list_next(lines);
2098      }
2099 
2100    _elm_code_widget_ensure_n_grid_rows(widget, n);
2101    _elm_code_widget_cell_size_get(widget, &cw, &ch);
2102 
2103    if (w*cw > ww)
2104      ww = w*cw;
2105    if (h*ch > wh)
2106      wh = h*ch;
2107 
2108    if (cw > 0 && ww/cw > w)
2109      pd->col_count = ww/cw;
2110    else
2111      pd->col_count = w;
2112 
2113    EINA_LIST_FOREACH(pd->grids, item, grid)
2114      {
2115         evas_object_textgrid_size_set(grid, pd->col_count, 1);
2116         evas_object_size_hint_min_set(grid, ww, ch);
2117      }
2118 
2119    /* Here we expand our scroller when there are less grids than lines of text. */
2120    elm_box_unpack(pd->gridbox, pd->expander);
2121    evas_object_size_hint_min_set(pd->expander, ww, (h * ch) - (eina_list_count(pd->grids) * ch));
2122    elm_box_pack_end(pd->gridbox, pd->expander);
2123 
2124    if (!newline && viewport)
2125      {
2126         /* Where possible render additional lines to the viewport. */
2127         _elm_code_widget_fill_range(widget, pd, first_row, last_row + 64, NULL);
2128         return;
2129      }
2130 
2131    if (EINA_DBL_EQ(pd->gravity_x, 1.0) || EINA_DBL_EQ(pd->gravity_y, 1.0))
2132      _elm_code_widget_scroll_by(widget,
2133         (EINA_DBL_EQ(pd->gravity_x, 1.0) && ww > old_width) ? ww - old_width : 0,
2134         (EINA_DBL_EQ(pd->gravity_y, 1.0) && wh > old_height) ? wh - old_height : 0);
2135 }
2136 
2137 EOAPI void
_elm_code_widget_line_refresh(Eo * obj,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)2138 _elm_code_widget_line_refresh(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
2139 {
2140    _elm_code_widget_fill_line(obj, pd, line);
2141 }
2142 
2143 EOAPI Eina_Bool
_elm_code_widget_line_visible_get(Eo * obj,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)2144 _elm_code_widget_line_visible_get(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
2145 {
2146    Evas_Coord cellh = 0, viewy = 0, viewh = 0;
2147 
2148    elm_scroller_region_get(pd->scroller, NULL, &viewy, NULL, &viewh);
2149    _elm_code_widget_cell_size_get(obj, NULL, &cellh);
2150 
2151    if (((int)line->number - 1) * cellh > viewy + viewh || (int)line->number * cellh < viewy)
2152      return EINA_FALSE;
2153 
2154    return EINA_TRUE;;
2155 }
2156 
2157 EOAPI unsigned int
_elm_code_widget_lines_visible_get(Eo * obj,Elm_Code_Widget_Data * pd)2158 _elm_code_widget_lines_visible_get(Eo *obj, Elm_Code_Widget_Data *pd)
2159 {
2160    Evas_Coord cellh = 0, viewh = 0;
2161 
2162    elm_scroller_region_get(pd->scroller, NULL, NULL, NULL, &viewh);
2163    _elm_code_widget_cell_size_get(obj, NULL, &cellh);
2164 
2165    if (cellh == 0)
2166      return 0;
2167    return viewh / cellh + 1;
2168 }
2169 
2170 EOLIAN static void
_elm_code_widget_font_set(Eo * obj,Elm_Code_Widget_Data * pd,const char * name,Evas_Font_Size size)2171 _elm_code_widget_font_set(Eo *obj, Elm_Code_Widget_Data *pd,
2172                           const char *name, Evas_Font_Size size)
2173 {
2174    Eina_List *item;
2175    Evas_Object *grid;
2176 
2177    const char *face = name;
2178    if (!face)
2179      face = "Mono";
2180 
2181    if (size == pd->font_size && !strcmp(face, pd->font_name))
2182      return;
2183 
2184    EINA_LIST_FOREACH(pd->grids, item, grid)
2185      {
2186         evas_object_textgrid_font_set(grid, face, size * elm_config_scale_get());
2187      }
2188 
2189    if (pd->cursor_rect && (eina_list_count(pd->grids) >= pd->cursor_line))
2190      _elm_code_widget_cursor_update(obj, pd);
2191 
2192    if (pd->font_name)
2193      eina_stringshare_del((char *)pd->font_name);
2194    pd->font_name = eina_stringshare_add(face);
2195    pd->font_size = size;
2196 }
2197 
2198 EOLIAN static void
_elm_code_widget_font_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,const char ** name,Evas_Font_Size * size)2199 _elm_code_widget_font_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd,
2200                           const char **name, Evas_Font_Size *size)
2201 {
2202    if (name)
2203      *name = strdup((const char *)pd->font_name);
2204    if (size)
2205      *size = pd->font_size;
2206 }
2207 
2208 EOLIAN static unsigned int
_elm_code_widget_columns_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2209 _elm_code_widget_columns_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2210 {
2211    return pd->col_count;
2212 }
2213 
2214 EOLIAN static void
_elm_code_widget_code_set(Eo * obj,Elm_Code_Widget_Data * pd,Elm_Code * code)2215 _elm_code_widget_code_set(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code *code)
2216 {
2217    EO_CONSTRUCTOR_CHECK_RETURN(obj);
2218 
2219    pd->code = code;
2220 
2221    if (code)
2222      code->widgets = eina_list_append(code->widgets, obj);
2223 }
2224 
2225 EOLIAN static Elm_Code *
_elm_code_widget_code_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2226 _elm_code_widget_code_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2227 {
2228    return pd->code;
2229 }
2230 
2231 EOLIAN static void
_elm_code_widget_gravity_set(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,double x,double y)2232 _elm_code_widget_gravity_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, double x, double y)
2233 {
2234    pd->gravity_x = x;
2235    pd->gravity_y = y;
2236 }
2237 
2238 EOLIAN static void
_elm_code_widget_gravity_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,double * x,double * y)2239 _elm_code_widget_gravity_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, double *x, double *y)
2240 {
2241    *x = pd->gravity_x;
2242    *y = pd->gravity_y;
2243 }
2244 
2245 EOLIAN static void
_elm_code_widget_policy_set(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,Elm_Code_Widget_Scroller_Policy policy_h,Elm_Code_Widget_Scroller_Policy policy_v)2246 _elm_code_widget_policy_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Elm_Code_Widget_Scroller_Policy policy_h, Elm_Code_Widget_Scroller_Policy policy_v)
2247 {
2248    elm_scroller_policy_set(pd->scroller, (Elm_Scroller_Policy)policy_h, (Elm_Scroller_Policy)policy_v);
2249 }
2250 
2251 EOLIAN static void
_elm_code_widget_policy_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,Elm_Code_Widget_Scroller_Policy * policy_h,Elm_Code_Widget_Scroller_Policy * policy_v)2252 _elm_code_widget_policy_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Elm_Code_Widget_Scroller_Policy *policy_h, Elm_Code_Widget_Scroller_Policy *policy_v)
2253 {
2254    elm_scroller_policy_get(pd->scroller, (Elm_Scroller_Policy*)policy_h, (Elm_Scroller_Policy*)policy_v);
2255 }
2256 
2257 EOLIAN static void
_elm_code_widget_tabstop_set(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,unsigned int tabstop)2258 _elm_code_widget_tabstop_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int tabstop)
2259 {
2260    pd->tabstop = tabstop;
2261    _elm_code_widget_fill(obj);
2262 }
2263 
2264 EOLIAN static unsigned int
_elm_code_widget_tabstop_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2265 _elm_code_widget_tabstop_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2266 {
2267    return pd->tabstop;
2268 }
2269 
2270 EOLIAN static void
_elm_code_widget_editable_set(Eo * obj,Elm_Code_Widget_Data * pd,Eina_Bool editable)2271 _elm_code_widget_editable_set(Eo *obj, Elm_Code_Widget_Data *pd, Eina_Bool editable)
2272 {
2273    pd->editable = editable;
2274    elm_object_focus_allow_set(obj, editable);
2275 }
2276 
2277 EOLIAN static Eina_Bool
_elm_code_widget_editable_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2278 _elm_code_widget_editable_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2279 {
2280    return pd->editable;
2281 }
2282 
2283 EOLIAN static void
_elm_code_widget_line_numbers_set(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,Eina_Bool line_numbers)2284 _elm_code_widget_line_numbers_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, Eina_Bool line_numbers)
2285 {
2286    pd->show_line_numbers = line_numbers;
2287 }
2288 
2289 EOLIAN static Eina_Bool
_elm_code_widget_line_numbers_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2290 _elm_code_widget_line_numbers_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2291 {
2292    return pd->show_line_numbers;
2293 }
2294 
2295 EOLIAN static void
_elm_code_widget_line_width_marker_set(Eo * obj,Elm_Code_Widget_Data * pd,unsigned int col)2296 _elm_code_widget_line_width_marker_set(Eo *obj, Elm_Code_Widget_Data *pd, unsigned int col)
2297 {
2298    pd->line_width_marker = col;
2299    _elm_code_widget_fill(obj);
2300 }
2301 
2302 EOLIAN static unsigned int
_elm_code_widget_line_width_marker_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2303 _elm_code_widget_line_width_marker_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2304 {
2305    return pd->line_width_marker;
2306 }
2307 
2308 EOLIAN static void
_elm_code_widget_show_whitespace_set(Eo * obj,Elm_Code_Widget_Data * pd,Eina_Bool show)2309 _elm_code_widget_show_whitespace_set(Eo *obj, Elm_Code_Widget_Data *pd, Eina_Bool show)
2310 {
2311    pd->show_whitespace = show;
2312    _elm_code_widget_fill(obj);
2313 }
2314 
2315 EOLIAN static Eina_Bool
_elm_code_widget_show_whitespace_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2316 _elm_code_widget_show_whitespace_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2317 {
2318    return pd->show_whitespace;
2319 }
2320 
2321 EOLIAN static void
_elm_code_widget_syntax_enabled_set(Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED,Eina_Bool enabled)2322 _elm_code_widget_syntax_enabled_set(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED,
2323                                     Eina_Bool enabled)
2324 {
2325    Elm_Code_Widget *widget = obj;
2326    Elm_Code *code;
2327 
2328    code = elm_code_widget_code_get(widget);
2329    if (enabled)
2330      elm_code_parser_standard_add(code, ELM_CODE_PARSER_STANDARD_SYNTAX);
2331    else
2332      code->parsers = eina_list_remove(code->parsers, ELM_CODE_PARSER_STANDARD_SYNTAX);
2333 
2334    _elm_code_parse_reset_file(code, code->file);
2335    _elm_code_widget_fill(obj);
2336 }
2337 
2338 EOLIAN static Eina_Bool
_elm_code_widget_syntax_enabled_get(const Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED)2339 _elm_code_widget_syntax_enabled_get(const Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED)
2340 {
2341    const Elm_Code_Widget *widget = obj;
2342    Elm_Code *code;
2343 
2344    code = elm_code_widget_code_get(widget);
2345    return !!eina_list_data_find(code->parsers, ELM_CODE_PARSER_STANDARD_SYNTAX);
2346 }
2347 
2348 EOLIAN static void
_elm_code_widget_tab_inserts_spaces_set(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,Eina_Bool spaces)2349 _elm_code_widget_tab_inserts_spaces_set(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd,
2350                                         Eina_Bool spaces)
2351 {
2352    pd->tab_inserts_spaces = spaces;
2353    if (!spaces)
2354      elm_code_widget_code_get(obj)->config.indent_style_efl = EINA_FALSE;
2355 }
2356 
2357 EOLIAN static Eina_Bool
_elm_code_widget_tab_inserts_spaces_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2358 _elm_code_widget_tab_inserts_spaces_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2359 {
2360    return pd->tab_inserts_spaces;
2361 }
2362 
2363 EOLIAN static void
_elm_code_widget_cursor_position_set(Eo * obj,Elm_Code_Widget_Data * pd,unsigned int row,unsigned int col)2364 _elm_code_widget_cursor_position_set(Eo *obj, Elm_Code_Widget_Data *pd, unsigned int row, unsigned int col)
2365 {
2366    _elm_code_widget_cursor_move(obj, pd, col, row);
2367 }
2368 
2369 EOLIAN static void
_elm_code_widget_cursor_position_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,unsigned int * row,unsigned int * col)2370 _elm_code_widget_cursor_position_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int *row, unsigned int *col)
2371 {
2372    *row = pd->cursor_line;
2373    *col = pd->cursor_col;
2374 }
2375 
2376 EOLIAN static void
_elm_code_widget_theme_refresh(Eo * obj,Elm_Code_Widget_Data * pd)2377 _elm_code_widget_theme_refresh(Eo *obj, Elm_Code_Widget_Data *pd)
2378 {
2379    Eo *edje;
2380    int r, g, b, a;
2381    unsigned int i;
2382    double fade;
2383    Evas_Object *grid;
2384 
2385    edje = elm_layout_edje_get(obj);
2386    edje_object_color_class_get(edje, "elm/code/status/default", &r, &g, &b, &a,
2387                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
2388 
2389    fade = (double) pd->alpha / 255;
2390    evas_object_color_set(pd->background, r * fade, g * fade, b * fade, a * fade);
2391 
2392    for (i = 0; i < eina_list_count(pd->grids); i++)
2393      {
2394         grid = eina_list_nth(pd->grids, i);
2395         _elm_code_widget_setup_palette(grid, obj, fade);
2396      }
2397 }
2398 
2399 EOLIAN static Eina_Error
_elm_code_widget_efl_ui_widget_theme_apply(Eo * obj,Elm_Code_Widget_Data * pd)2400 _elm_code_widget_efl_ui_widget_theme_apply(Eo *obj, Elm_Code_Widget_Data *pd)
2401 {
2402    if (efl_ui_widget_theme_apply(efl_cast(obj, EFL_UI_WIDGET_CLASS)) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
2403      return EFL_UI_THEME_APPLY_ERROR_GENERIC;
2404 
2405    _elm_code_widget_theme_refresh(obj, pd);
2406 
2407    return EFL_UI_THEME_APPLY_ERROR_NONE;
2408 }
2409 
2410 EOLIAN static int
_elm_code_widget_alpha_get(const Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)2411 _elm_code_widget_alpha_get(const Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
2412 {
2413    return pd->alpha;
2414 }
2415 
2416 EOLIAN static void
_elm_code_widget_alpha_set(Eo * obj,Elm_Code_Widget_Data * pd,int alpha)2417 _elm_code_widget_alpha_set(Eo *obj, Elm_Code_Widget_Data *pd, int alpha)
2418 {
2419    pd->alpha = alpha;
2420 
2421    _elm_code_widget_efl_ui_widget_theme_apply(obj, pd);
2422 }
2423 
2424 EOLIAN static void
_elm_code_widget_efl_canvas_group_group_add(Eo * obj,Elm_Code_Widget_Data * pd)2425 _elm_code_widget_efl_canvas_group_group_add(Eo *obj, Elm_Code_Widget_Data *pd)
2426 {
2427    Evas_Object *gridrows, *scroller, *background;
2428    const char *fontname, *fontsize;
2429    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
2430 
2431    efl_canvas_group_add(efl_super(obj, ELM_CODE_WIDGET_CLASS));
2432    if (!elm_widget_theme_klass_get(obj))
2433      elm_widget_theme_klass_set(obj, "code");
2434    elm_widget_theme_element_set(obj, "layout");
2435 
2436    legacy_object_focus_handle(obj);
2437    elm_widget_can_focus_set(obj, EINA_TRUE);
2438    pd->alpha = 255;
2439 
2440    if (elm_widget_theme_object_set(obj, wd->resize_obj,
2441                                        elm_widget_theme_klass_get(obj),
2442                                        elm_widget_theme_element_get(obj),
2443                                        elm_widget_theme_style_get(obj)) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
2444      CRI("Failed to set layout!");
2445 
2446    scroller = elm_scroller_add(obj);
2447    evas_object_size_hint_weight_set(scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
2448    evas_object_size_hint_align_set(scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
2449    evas_object_show(scroller);
2450    elm_layout_content_set(obj, "elm.swallow.content", scroller);
2451    elm_object_focus_allow_set(scroller, EINA_FALSE);
2452    pd->scroller = scroller;
2453    evas_object_event_callback_add(scroller, EVAS_CALLBACK_MOUSE_DOWN,
2454                                   _elm_code_widget_scroller_clicked_cb, obj);
2455 
2456    background = evas_object_rectangle_add(evas_object_evas_get(scroller));
2457    evas_object_size_hint_weight_set(background, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
2458    evas_object_size_hint_align_set(background, EVAS_HINT_FILL, EVAS_HINT_FILL);
2459    evas_object_show(background);
2460    elm_object_part_content_set(scroller, "elm.swallow.background", background);
2461    pd->background = background;
2462 
2463    fontname = edje_object_data_get(elm_layout_edje_get(obj), "font.name");
2464    fontsize = edje_object_data_get(elm_layout_edje_get(obj), "font.size");
2465    if (fontname && fontsize)
2466      _elm_code_widget_font_set(obj, pd, fontname, atoi(fontsize));
2467 
2468    gridrows = elm_box_add(scroller);
2469    evas_object_size_hint_weight_set(gridrows, EVAS_HINT_EXPAND, 0.0);
2470    evas_object_size_hint_align_set(gridrows, EVAS_HINT_FILL, 0.0);
2471    elm_box_align_set(gridrows, 0.0, 0.0);
2472    elm_object_content_set(scroller, gridrows);
2473    pd->gridbox = gridrows;
2474 
2475    pd->expander = evas_object_rectangle_add(evas_object_evas_get(scroller));
2476    elm_box_pack_end(pd->gridbox, pd->expander);
2477 
2478    _elm_code_widget_efl_ui_widget_theme_apply(obj, pd);
2479 
2480    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _elm_code_widget_resize_cb, obj);
2481    evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, _elm_code_widget_key_down_cb, obj);
2482    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _elm_code_widget_hidden_cb, obj);
2483    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _elm_code_widget_show_cb, obj);
2484 
2485    evas_object_smart_callback_add(obj, "focused", _elm_code_widget_focused_event_cb, obj);
2486    evas_object_smart_callback_add(obj, "unfocused", _elm_code_widget_unfocused_event_cb, obj);
2487    evas_object_smart_callback_add(scroller, "scroll", _elm_code_widget_scroll_event_cb, obj);
2488 
2489    efl_event_callback_add(obj, &ELM_CODE_EVENT_LINE_LOAD_DONE, _elm_code_widget_line_cb, obj);
2490    efl_event_callback_add(obj, &ELM_CODE_EVENT_FILE_LOAD_DONE, _elm_code_widget_file_cb, obj);
2491    efl_event_callback_add(obj, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CHANGED, _elm_code_widget_selection_cb, obj);
2492    efl_event_callback_add(obj, EFL_UI_CODE_WIDGET_EVENT_CODE_SELECTION_CLEARED, _elm_code_widget_selection_clear_cb, obj);
2493 }
2494 
2495 /* Internal EO APIs and hidden overrides */
2496 
2497 #define ELM_CODE_WIDGET_EXTRA_OPS \
2498    EFL_CANVAS_GROUP_ADD_OPS(elm_code_widget)
2499 
2500 #include "elm_code_widget_text.c"
2501 #include "elm_code_widget_undo.c"
2502 #include "elm_code_widget.eo.c"
2503 #include "elm_code_widget_eo.legacy.c"
2504