1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #include "Elementary.h"
6
7 #include "elm_code_widget_private.h"
8
9 static int
_elm_code_widget_text_line_number_width_get(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)10 _elm_code_widget_text_line_number_width_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
11 {
12 int max;
13
14 max = elm_code_file_lines_get(pd->code->file);
15
16 // leave space for 2 digits minimum
17 if (max < 10)
18 max = 10;
19
20 return floor(log10(max)) + 1;
21 }
22
23 static int
_elm_code_widget_text_left_gutter_width_get(Eo * obj,Elm_Code_Widget_Data * pd)24 _elm_code_widget_text_left_gutter_width_get(Eo *obj, Elm_Code_Widget_Data *pd)
25 {
26 Elm_Code_Widget *widget;
27 int width = 1; // the status icon, for now
28
29 widget = obj;
30 if (!widget)
31 return width;
32
33 if (pd->show_line_numbers)
34 width += _elm_code_widget_text_line_number_width_get(widget, pd);
35
36 return width;
37 }
38
39 static char *
_elm_code_widget_text_multi_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_line,unsigned int end_col)40 _elm_code_widget_text_multi_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
41 unsigned int start_line, unsigned int start_col,
42 unsigned int end_line, unsigned int end_col)
43 {
44 Elm_Code_Line *line;
45 char *first, *last, *ret, *ptr;
46 const char *newline;
47 short newline_len;
48 int ret_len;
49 unsigned int row, start, end;
50
51 newline = elm_code_file_line_ending_chars_get(pd->code->file, &newline_len);
52
53 line = elm_code_file_line_get(pd->code->file, start_line);
54 start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
55 first = elm_code_line_text_substr(line, start, line->length - start);
56
57 line = elm_code_file_line_get(pd->code->file, end_line);
58 end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
59 last = elm_code_line_text_substr(line, 0, end);
60
61 ret_len = strlen(first) + strlen(last) + newline_len;
62
63 for (row = start_line + 1; row < end_line; row++)
64 {
65 line = elm_code_file_line_get(pd->code->file, row);
66 ret_len += line->length + newline_len;
67 }
68
69 ret = malloc(sizeof(char) * (ret_len + 1));
70 if (!ret) goto end;
71
72 snprintf(ret, strlen(first) + newline_len + 1, "%s%s", first, newline);
73
74 ptr = ret;
75 ptr += strlen(first) + newline_len;
76
77 for (row = start_line + 1; row < end_line; row++)
78 {
79 line = elm_code_file_line_get(pd->code->file, row);
80 if (line->length > 0)
81 snprintf(ptr, line->length + 1, "%s", elm_code_line_text_get(line, NULL));
82
83 snprintf(ptr + line->length, newline_len + 1, "%s", newline);
84 ptr += line->length + newline_len;
85 }
86 snprintf(ptr, strlen(last) + 1, "%s", last);
87
88 end:
89 free(first);
90 free(last);
91 return ret;
92 }
93
94 static char *
_elm_code_widget_text_single_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_col)95 _elm_code_widget_text_single_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
96 unsigned int start_line, unsigned int start_col,
97 unsigned int end_col)
98 {
99 Elm_Code_Line *line;
100 unsigned int start, end;
101
102 line = elm_code_file_line_get(pd->code->file, start_line);
103 start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
104 end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
105
106 return elm_code_line_text_substr(line, start, end - start);
107 }
108
109 static char *
_elm_code_widget_text_between_positions_get(Eo * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_line,unsigned int end_col)110 _elm_code_widget_text_between_positions_get(Eo *widget, Elm_Code_Widget_Data *pd,
111 unsigned int start_line, unsigned int start_col,
112 unsigned int end_line, unsigned int end_col)
113 {
114 if (start_line == end_line)
115 return _elm_code_widget_text_single_get(widget, pd, start_line, start_col, end_col);
116 else
117 return _elm_code_widget_text_multi_get(widget, pd, start_line, start_col, end_line, end_col);
118 }
119
120 static unsigned int
_elm_code_widget_line_text_column_width_to_position(Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED,Elm_Code_Line * line,unsigned int position)121 _elm_code_widget_line_text_column_width_to_position(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int position)
122 {
123 Eina_Unicode unicode;
124 unsigned int count = 1;
125 int index = 0;
126 const char *chars;
127
128 if (line->length == 0)
129 return 1;
130
131 if (line->modified)
132 chars = line->modified;
133 else
134 chars = line->content;
135 if (position > line->length)
136 position = line->length;
137
138 while ((unsigned int) index < position)
139 {
140 unicode = eina_unicode_utf8_next_get(chars, &index);
141 if (unicode == 0)
142 break;
143
144 if (unicode == '\t')
145 count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
146 else
147 count++;
148 }
149
150 return count;
151 }
152
153 static unsigned int
_elm_code_widget_line_text_column_width_get(Eo * obj,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)154 _elm_code_widget_line_text_column_width_get(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
155 {
156 if (!line)
157 return 0;
158
159 return _elm_code_widget_line_text_column_width_to_position(obj, pd, line, line->length) - 1;
160 }
161
162 static unsigned int
_elm_code_widget_line_text_position_for_column_get(Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED,Elm_Code_Line * line,unsigned int column)163 _elm_code_widget_line_text_position_for_column_get(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int column)
164 {
165 Eina_Unicode unicode;
166 unsigned int count = 1, position = 0;
167 int index = 0;
168 const char *chars;
169
170 if (!line || line->length == 0 || column == 1)
171 return 0;
172
173 if (line->modified)
174 chars = line->modified;
175 else
176 chars = line->content;
177
178 while ((unsigned int) count <= column && index <= (int) line->length)
179 {
180 position = (unsigned int) index;
181 if (index < (int) line->length)
182 unicode = eina_unicode_utf8_next_get(chars, &index);
183 else return line->length;
184
185 if (unicode == 0)
186 return line->length;
187 else if (unicode == '\t')
188 count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
189 else
190 count++;
191 }
192
193 return position;
194 }
195
196 static unsigned int
_elm_code_widget_text_tabwidth_at_column_get(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,unsigned int column)197 _elm_code_widget_text_tabwidth_at_column_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int column)
198 {
199 return pd->tabstop - ((column - 1) % pd->tabstop);
200 }
201
202 static void
_elm_code_widget_text_insert_single(Elm_Code_Widget * widget,Elm_Code * code,unsigned int col,unsigned int row,const char * text,unsigned int len)203 _elm_code_widget_text_insert_single(Elm_Code_Widget *widget, Elm_Code *code,
204 unsigned int col, unsigned int row, const char *text, unsigned int len)
205 {
206 Elm_Code_Widget_Data *pd;
207 Elm_Code_Line *line;
208 unsigned int position, newcol;
209
210 pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
211 line = elm_code_file_line_get(code->file, row);
212 position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
213 elm_code_line_text_insert(line, position, text, len);
214
215 newcol = elm_code_widget_line_text_column_width_to_position(widget, line, position + len);
216
217 // if we are making a line longer than before then we need to resize
218 if (newcol > pd->col_count)
219 _elm_code_widget_resize(widget, line);
220
221 efl_ui_code_widget_cursor_position_set(widget, row, newcol);
222 }
223
224 static void
_elm_code_widget_text_insert_multi(Elm_Code_Widget * widget,Elm_Code * code,unsigned int col,unsigned int row,const char * text,unsigned int len)225 _elm_code_widget_text_insert_multi(Elm_Code_Widget *widget, Elm_Code *code,
226 unsigned int col, unsigned int row, const char *text, unsigned int len)
227 {
228 Elm_Code_Line *line;
229 unsigned int position, newrow, remain;
230 int nlpos;
231 short nllen;
232 char *ptr;
233
234 line = elm_code_file_line_get(code->file, row);
235 position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
236 elm_code_line_split_at(line, position);
237
238 newrow = row;
239 ptr = (char *)text;
240 remain = len;
241 while ((nlpos = elm_code_text_newlinenpos(ptr, remain, &nllen)) != ELM_CODE_TEXT_NOT_FOUND)
242 {
243 if (newrow == row)
244 _elm_code_widget_text_insert_single(widget, code, col, row, text, nlpos);
245 else
246 elm_code_file_line_insert(code->file, newrow, ptr, nlpos, NULL);
247
248 remain -= nlpos + nllen;
249 ptr += nlpos + nllen;
250 newrow++;
251 }
252
253 _elm_code_widget_text_insert_single(widget, code, 1, newrow, ptr, len - (ptr - text));
254 }
255
256 void
_elm_code_widget_text_at_cursor_insert_do(Elm_Code_Widget * widget,const char * text,int length,Eina_Bool undo)257 _elm_code_widget_text_at_cursor_insert_do(Elm_Code_Widget *widget, const char *text, int length, Eina_Bool undo)
258 {
259 Elm_Code *code;
260 Elm_Code_Line *line;
261 Elm_Code_Widget_Change_Info *change;
262 unsigned int row, col, end_row, end_col, curlen, indent;
263 const char *curtext, *indent_text;
264
265 if (undo)
266 elm_code_widget_selection_delete(widget);
267
268 code = efl_ui_code_widget_code_get(widget);
269 efl_ui_code_widget_cursor_position_get(widget, &row, &col);
270 line = elm_code_file_line_get(code->file, row);
271 if (line == NULL)
272 {
273 elm_code_file_line_append(code->file, "", 0, NULL);
274 row = elm_code_file_lines_get(code->file);
275 line = elm_code_file_line_get(code->file, row);
276 }
277 if (text[0] == '}')
278 {
279 curtext = elm_code_line_text_get(line, &curlen);
280
281 if (elm_code_text_is_whitespace(curtext, line->length))
282 {
283 indent_text = elm_code_line_indent_matching_braces_get(line, &indent);
284 elm_code_line_text_leading_whitespace_strip(line);
285
286 if (indent > 0)
287 elm_code_line_text_insert(line, 0, indent_text, indent);
288
289 col = elm_code_widget_line_text_column_width_to_position(widget, line, indent + 1);
290 efl_ui_code_widget_cursor_position_set(widget, row, col);
291 }
292 }
293
294 if (elm_code_text_newlinenpos(text, length, NULL) == ELM_CODE_TEXT_NOT_FOUND)
295 _elm_code_widget_text_insert_single(widget, code, col, row, text, length);
296 else
297 _elm_code_widget_text_insert_multi(widget, code, col, row, text, length);
298 efl_ui_code_widget_cursor_position_get(widget, &end_row, &end_col);
299
300 efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
301
302 if (undo)
303 {
304 change = _elm_code_widget_change_create(col, row, end_col - 1, end_row, text, length, EINA_TRUE);
305 _elm_code_widget_undo_change_add(widget, change);
306 _elm_code_widget_change_free(change);
307 }
308 }
309
310 EOLIAN void
_elm_code_widget_text_at_cursor_insert(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd EINA_UNUSED,const char * text)311 _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd EINA_UNUSED, const char *text)
312 {
313 _elm_code_widget_text_at_cursor_insert_do(widget, text, strlen(text), EINA_TRUE);
314 }
315
316 void
_elm_code_widget_text_at_cursor_insert_no_undo(Elm_Code_Widget * widget,const char * text,unsigned int length)317 _elm_code_widget_text_at_cursor_insert_no_undo(Elm_Code_Widget *widget, const char *text, unsigned int length)
318 {
319 _elm_code_widget_text_at_cursor_insert_do(widget, text, length, EINA_FALSE);
320 }
321