1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_edit_scroll.h"
4 
5 #include <pobl/bl_util.h>
6 
7 #include "vt_edit_util.h"
8 
9 /* --- static functions --- */
10 
11 /*
12  * src and dst may overlap
13  */
copy_lines(vt_edit_t * edit,int dst_row,int src_row,u_int size,int mark_changed)14 static int copy_lines(vt_edit_t *edit, int dst_row, int src_row, u_int size, int mark_changed) {
15   int count;
16   vt_line_t *src_line;
17   vt_line_t *dst_line;
18 
19   if (size == 0 || dst_row == src_row) {
20     return 1;
21   }
22 
23   if (src_row + size > edit->model.num_rows) {
24 #ifdef DEBUG
25     bl_warn_printf(BL_DEBUG_TAG " copying %d lines from %d row is over edit->model.num_rows(%d)",
26                    size, src_row, edit->model.num_rows);
27 #endif
28 
29     size = edit->model.num_rows - src_row;
30 
31 #ifdef DEBUG
32     bl_msg_printf(" ... size modified -> %d.\n", size);
33 #endif
34   }
35 
36   if (dst_row + size > edit->model.num_rows) {
37 #ifdef DEBUG
38     bl_warn_printf(BL_DEBUG_TAG " copying %d lines to %d row is over edit->model.num_rows(%d)",
39                    size, dst_row, edit->model.num_rows);
40 #endif
41 
42     size = edit->model.num_rows - dst_row;
43 
44 #ifdef DEBUG
45     bl_msg_printf(" ... size modified -> %d.\n", size);
46 #endif
47   }
48 
49   if (dst_row < src_row) {
50     for (count = 0; count < size; count++) {
51       dst_line = vt_model_get_line(&edit->model, dst_row + count);
52       src_line = vt_model_get_line(&edit->model, src_row + count);
53 
54       vt_line_swap(dst_line, src_line);
55       if (mark_changed) {
56         vt_line_set_modified_all(dst_line);
57       }
58     }
59   } else {
60     for (count = size - 1; count >= 0; count--) {
61       dst_line = vt_model_get_line(&edit->model, dst_row + count);
62       src_line = vt_model_get_line(&edit->model, src_row + count);
63 
64       vt_line_swap(dst_line, src_line);
65       if (mark_changed) {
66         vt_line_set_modified_all(dst_line);
67       }
68     }
69   }
70 
71   return 1;
72 }
73 
clear_lines_to_eol(vt_edit_t * edit,int beg_row,u_int size)74 static int clear_lines_to_eol(vt_edit_t *edit, int beg_row, u_int size) {
75   int count;
76 
77   vt_edit_clear_lines(edit, beg_row, size);
78 
79   for (count = 0; count < size; count++) {
80     vt_line_set_modified_all(vt_model_get_line(&edit->model, beg_row + count));
81   }
82 
83   return 1;
84 }
85 
scroll_upward_region(vt_edit_t * edit,int boundary_beg,int boundary_end,u_int size)86 static int scroll_upward_region(vt_edit_t *edit, int boundary_beg, int boundary_end, u_int size) {
87   int count;
88   int window_is_scrolled;
89 
90   if (boundary_beg + size > boundary_end) {
91     /*
92      * all lines within boundary are scrolled out.
93      */
94 
95     if (edit->is_logging) {
96       for (count = boundary_beg; count < boundary_end; count++) {
97         (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
98                                                             vt_model_get_line(&edit->model, count));
99       }
100     }
101 
102     edit->cursor.row = boundary_beg;
103     edit->cursor.col = 0;
104     edit->cursor.char_index = 0;
105 
106     (*edit->scroll_listener->scrolled_out_lines_finished)(edit->scroll_listener->self);
107 
108     return clear_lines_to_eol(edit, boundary_beg, boundary_end - boundary_beg + 1);
109   }
110 
111 /*
112  * scrolling up in window.
113  *
114  * !! Notice !!
115  * This should be done before vt_edit_t data structure is chanegd
116  * for the listener object to clear existing cache.
117  */
118 #if 0
119   bl_debug_printf(" SCROLL UP region %d %d size %d\n", boundary_beg, boundary_end, size);
120 #endif
121   window_is_scrolled = (*edit->scroll_listener->window_scroll_upward_region)(
122       edit->scroll_listener->self, boundary_beg, boundary_end, size);
123 
124   /*
125    * handing over scrolled out lines , and calculating scrolling beg/end y
126    * positions.
127    */
128 
129   if (edit->is_logging) {
130     for (count = boundary_beg; count < boundary_beg + size; count++) {
131       (*edit->scroll_listener->receive_scrolled_out_line)(edit->scroll_listener->self,
132                                                           vt_model_get_line(&edit->model, count));
133     }
134   }
135 
136   /*
137    * resetting cursor position.
138    */
139 
140   if (boundary_beg <= edit->cursor.row && edit->cursor.row <= boundary_end) {
141     if (edit->cursor.row < boundary_beg + size) {
142       edit->cursor.row = boundary_beg;
143       edit->cursor.char_index = 0;
144       edit->cursor.col = 0;
145     } else {
146       edit->cursor.row -= size;
147     }
148   }
149 
150   /*
151    * scrolling up in edit.
152    */
153 
154   if (boundary_beg == 0 && boundary_end == vt_model_end_row(&edit->model)) {
155     vt_model_scroll_upward(&edit->model, size);
156   } else {
157     copy_lines(edit, boundary_beg, boundary_beg + size, boundary_end - (boundary_beg + size) + 1,
158                0);
159   }
160 
161   if (!window_is_scrolled) {
162     int count;
163 
164     vt_edit_clear_lines(edit, boundary_end - size + 1, size);
165 
166     for (count = boundary_beg; count <= boundary_end; count++) {
167       vt_line_set_modified_all(vt_model_get_line(&edit->model, count));
168     }
169   } else {
170     clear_lines_to_eol(edit, boundary_end - size + 1, size);
171   }
172 
173   /*
174    * This must be called after vt_model_scroll_upward() because
175    * scrolled_out_lines_finished()
176    * can change vt_model_t.
177    */
178   (*edit->scroll_listener->scrolled_out_lines_finished)(edit->scroll_listener->self);
179 
180   return 1;
181 }
182 
scroll_downward_region(vt_edit_t * edit,int boundary_beg,int boundary_end,u_int size)183 static int scroll_downward_region(vt_edit_t *edit, int boundary_beg, int boundary_end, u_int size) {
184   int window_is_scrolled;
185 
186   if (boundary_beg + size > boundary_end) {
187     /*
188      * all lines within boundary are scrolled out.
189      */
190 
191     edit->cursor.row = boundary_end;
192     edit->cursor.col = 0;
193     edit->cursor.char_index = 0;
194 
195     return clear_lines_to_eol(edit, boundary_beg, boundary_end - boundary_beg + 1);
196   }
197 
198 /*
199  * scrolling down in window.
200  *
201  * !! Notice !!
202  * This should be done before vt_edit_t data structure is chanegd
203  * for the listener object to clear existing cache.
204  */
205 #if 0
206   bl_debug_printf(" SCROLL DOWN region %d %d size %d\n", boundary_beg, boundary_end, size);
207 #endif
208   window_is_scrolled = (*edit->scroll_listener->window_scroll_downward_region)(
209       edit->scroll_listener->self, boundary_beg, boundary_end, size);
210 
211   /*
212    * resetting cursor position.
213    */
214   if (boundary_beg <= edit->cursor.row && edit->cursor.row <= boundary_end) {
215     if (edit->cursor.row + size >= boundary_end + 1) {
216       edit->cursor.row = boundary_end;
217       edit->cursor.char_index = 0;
218       edit->cursor.col = 0;
219     } else {
220       edit->cursor.row += size;
221     }
222   }
223 
224   /*
225    * scrolling down in edit.
226    */
227 
228   if (boundary_beg == 0 && boundary_end == vt_model_end_row(&edit->model)) {
229     vt_model_scroll_downward(&edit->model, size);
230   } else {
231     copy_lines(edit, boundary_beg + size, boundary_beg, (boundary_end - size) - boundary_beg + 1,
232                0);
233   }
234 
235   if (!window_is_scrolled) {
236     int count;
237 
238     vt_edit_clear_lines(edit, boundary_beg, size);
239 
240     for (count = boundary_beg; count <= boundary_end; count++) {
241       vt_line_set_modified_all(vt_model_get_line(&edit->model, count));
242     }
243   } else {
244     clear_lines_to_eol(edit, boundary_beg, size);
245   }
246 
247   return 1;
248 }
249 
250 /* --- global functions --- */
251 
vt_edsl_scroll_upward(vt_edit_t * edit,u_int size)252 int vt_edsl_scroll_upward(vt_edit_t *edit, u_int size) {
253 #if 0
254   /*
255    * XXX
256    * Can this cause unexpected result ?
257    */
258   if (edit->vmargin_beg > edit->cursor.row || edit->cursor.row > edit->vmargin_end) {
259     return 0;
260   }
261 #endif
262 
263   return scroll_upward_region(edit, edit->vmargin_beg, edit->vmargin_end, size);
264 }
265 
vt_edsl_scroll_downward(vt_edit_t * edit,u_int size)266 int vt_edsl_scroll_downward(vt_edit_t *edit, u_int size) {
267 #if 0
268   /*
269    * XXX
270    * Can this cause unexpected result ?
271    */
272   if (edit->vmargin_beg > edit->cursor.row || edit->cursor.row > edit->vmargin_end) {
273     return 0;
274   }
275 #endif
276 
277   return scroll_downward_region(edit, edit->vmargin_beg, edit->vmargin_end, size);
278 }
279 
280 /*
281  * XXX
282  * not used for now.
283  */
284 #if 0
285 int vt_edsl_scroll_upward_in_all(vt_edit_t *edit, u_int size) {
286   return scroll_upward_region(edit, 0, edit->model.num_rows - 1, size);
287 }
288 
289 int vt_edsl_scroll_downward_in_all(vt_edit_t *edit, u_int size) {
290   return scroll_downward_region(edit, 0, edit->model.num_rows - 1, size);
291 }
292 #endif
293 
vt_is_scroll_upperlimit(vt_edit_t * edit,int row)294 int vt_is_scroll_upperlimit(vt_edit_t *edit, int row) { return (row == edit->vmargin_beg); }
295 
vt_is_scroll_lowerlimit(vt_edit_t * edit,int row)296 int vt_is_scroll_lowerlimit(vt_edit_t *edit, int row) { return (row == edit->vmargin_end); }
297 
vt_edsl_insert_new_line(vt_edit_t * edit)298 int vt_edsl_insert_new_line(vt_edit_t *edit) {
299   int start_row;
300   int start_col;
301   int end_row;
302 
303   if (edit->cursor.row < edit->vmargin_beg || edit->vmargin_end < edit->cursor.row) {
304     return 0;
305   }
306 
307   start_row = edit->cursor.row;
308   start_col = edit->cursor.col;
309   end_row = edit->vmargin_end;
310 
311   scroll_downward_region(edit, start_row, end_row, 1);
312   vt_cursor_goto_by_col(&edit->cursor, start_col, start_row);
313   vt_edit_clear_line_to_right(edit);
314 
315   return 1;
316 }
317 
vt_edsl_delete_line(vt_edit_t * edit)318 int vt_edsl_delete_line(vt_edit_t *edit) {
319   int start_row;
320   int start_col;
321   int end_row;
322   int is_logging;
323 
324   if (edit->cursor.row < edit->vmargin_beg || edit->vmargin_end < edit->cursor.row) {
325     return 0;
326   }
327 
328   is_logging = edit->is_logging;
329   edit->is_logging = 0;
330 
331   start_row = edit->cursor.row;
332   start_col = edit->cursor.col;
333   end_row = edit->vmargin_end;
334 
335   scroll_upward_region(edit, start_row, end_row, 1);
336   vt_edit_clear_lines(edit, end_row, 1);
337   vt_cursor_goto_by_col(&edit->cursor, start_col, start_row);
338 
339   edit->is_logging = is_logging;
340 
341   return 1;
342 }
343