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