154
|
1 /* Calculate what line insertion or deletion to do, and do it,
|
7307
|
2 Copyright (C) 1985, 1986, 1990, 1993, 1994 Free Software Foundation, Inc.
|
154
|
3
|
|
4 This file is part of GNU Emacs.
|
|
5
|
|
6 GNU Emacs is free software; you can redistribute it and/or modify
|
|
7 it under the terms of the GNU General Public License as published by
|
732
|
8 the Free Software Foundation; either version 2, or (at your option)
|
154
|
9 any later version.
|
|
10
|
|
11 GNU Emacs is distributed in the hope that it will be useful,
|
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14 GNU General Public License for more details.
|
|
15
|
|
16 You should have received a copy of the GNU General Public License
|
|
17 along with GNU Emacs; see the file COPYING. If not, write to
|
|
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
19
|
|
20
|
4696
|
21 #include <config.h>
|
154
|
22 #include "termchar.h"
|
|
23 #include "lisp.h"
|
|
24 #include "dispextern.h"
|
766
|
25 #include "frame.h"
|
154
|
26
|
|
27 extern struct display_line **ophys_lines;
|
|
28
|
|
29 #define max(a, b) ((a) > (b) ? (a) : (b))
|
|
30 #define min(a, b) ((a) < (b) ? (a) : (b))
|
|
31
|
|
32 /* All costs measured in characters.
|
766
|
33 So no cost can exceed the area of a frame, measured in characters.
|
6773
|
34 Let's hope this is never more than 1000000 characters. */
|
154
|
35
|
6773
|
36 #define INFINITY 1000000
|
154
|
37
|
|
38 struct matrix_elt
|
|
39 {
|
|
40 /* Cost of outputting through this line
|
|
41 if no insert/delete is done just above it. */
|
6773
|
42 int writecost;
|
154
|
43 /* Cost of outputting through this line
|
|
44 if an insert is done just above it. */
|
6773
|
45 int insertcost;
|
154
|
46 /* Cost of outputting through this line
|
|
47 if a delete is done just above it. */
|
6773
|
48 int deletecost;
|
154
|
49 /* Number of inserts so far in this run of inserts,
|
|
50 for the cost in insertcost. */
|
6773
|
51 unsigned char insertcount;
|
154
|
52 /* Number of deletes so far in this run of deletes,
|
|
53 for the cost in deletecost. */
|
6773
|
54 unsigned char deletecount;
|
10262
|
55 /* Number of writes so far since the last insert
|
|
56 or delete for the cost in writecost. */
|
|
57 unsigned char writecount;
|
154
|
58 };
|
|
59
|
|
60
|
10262
|
61 /* Determine, in matrix[i,j], the cost of updating the first j old
|
|
62 lines into the first i new lines using the general scrolling method.
|
154
|
63 This involves using insert or delete somewhere if i != j.
|
|
64 For each matrix elements, three kinds of costs are recorded:
|
|
65 the smallest cost that ends with an insert, the smallest
|
|
66 cost that ends with a delete, and the smallest cost that
|
|
67 ends with neither one. These are kept separate because
|
|
68 on some terminals the cost of doing an insert varies
|
|
69 depending on whether one was just done, etc. */
|
|
70
|
|
71 /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
|
|
72 old_hash[VPOS] is the hash code of the old line at VPOS.
|
|
73 new_hash[VPOS] is the hash code of the new line at VPOS.
|
766
|
74 Note that these are not true frame vpos's, but relative
|
154
|
75 to the place at which the first mismatch between old and
|
|
76 new contents appears. */
|
|
77
|
|
78 static void
|
766
|
79 calculate_scrolling (frame, matrix, window_size, lines_below,
|
154
|
80 draw_cost, old_hash, new_hash,
|
|
81 free_at_end)
|
766
|
82 FRAME_PTR frame;
|
154
|
83 /* matrix is of size window_size + 1 on each side. */
|
|
84 struct matrix_elt *matrix;
|
|
85 int window_size;
|
|
86 int *draw_cost;
|
|
87 int *old_hash;
|
|
88 int *new_hash;
|
|
89 int free_at_end;
|
|
90 {
|
|
91 register int i, j;
|
766
|
92 int frame_height = FRAME_HEIGHT (frame);
|
154
|
93 register struct matrix_elt *p, *p1;
|
|
94 register int cost, cost1;
|
|
95
|
|
96 int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below);
|
|
97 /* first_insert_cost[I] is the cost of doing the first insert-line
|
|
98 at the I'th line of the lines we are considering,
|
|
99 where I is origin 1 (as it is below). */
|
|
100 int *first_insert_cost
|
766
|
101 = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved];
|
154
|
102 int *first_delete_cost
|
766
|
103 = &FRAME_DELETE_COST (frame)[frame_height - 1 - lines_moved];
|
154
|
104 int *next_insert_cost
|
766
|
105 = &FRAME_INSERTN_COST (frame)[frame_height - 1 - lines_moved];
|
154
|
106 int *next_delete_cost
|
766
|
107 = &FRAME_DELETEN_COST (frame)[frame_height - 1 - lines_moved];
|
154
|
108
|
|
109 /* Discourage long scrolls on fast lines.
|
766
|
110 Don't scroll nearly a full frame height unless it saves
|
154
|
111 at least 1/4 second. */
|
766
|
112 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
|
154
|
113
|
3356
|
114 if (baud_rate <= 0)
|
|
115 extra_cost = 1;
|
|
116
|
154
|
117 /* initialize the top left corner of the matrix */
|
|
118 matrix->writecost = 0;
|
|
119 matrix->insertcost = INFINITY;
|
|
120 matrix->deletecost = INFINITY;
|
|
121 matrix->insertcount = 0;
|
|
122 matrix->deletecount = 0;
|
|
123
|
|
124 /* initialize the left edge of the matrix */
|
|
125 cost = first_insert_cost[1] - next_insert_cost[1];
|
|
126 for (i = 1; i <= window_size; i++)
|
|
127 {
|
|
128 p = matrix + i * (window_size + 1);
|
|
129 cost += draw_cost[i] + next_insert_cost[i] + extra_cost;
|
|
130 p->insertcost = cost;
|
|
131 p->writecost = INFINITY;
|
|
132 p->deletecost = INFINITY;
|
|
133 p->insertcount = i;
|
|
134 p->deletecount = 0;
|
|
135 }
|
|
136
|
|
137 /* initialize the top edge of the matrix */
|
|
138 cost = first_delete_cost[1] - next_delete_cost[1];
|
|
139 for (j = 1; j <= window_size; j++)
|
|
140 {
|
|
141 cost += next_delete_cost[j];
|
|
142 matrix[j].deletecost = cost;
|
|
143 matrix[j].writecost = INFINITY;
|
|
144 matrix[j].insertcost = INFINITY;
|
|
145 matrix[j].deletecount = j;
|
|
146 matrix[j].insertcount = 0;
|
|
147 }
|
|
148
|
766
|
149 /* `i' represents the vpos among new frame contents.
|
|
150 `j' represents the vpos among the old frame contents. */
|
154
|
151 p = matrix + window_size + 2; /* matrix [1, 1] */
|
|
152 for (i = 1; i <= window_size; i++, p++)
|
|
153 for (j = 1; j <= window_size; j++, p++)
|
|
154 {
|
|
155 /* p contains the address of matrix [i, j] */
|
|
156
|
|
157 /* First calculate the cost assuming we do
|
|
158 not insert or delete above this line.
|
|
159 That is, if we update through line i-1
|
|
160 based on old lines through j-1,
|
|
161 and then just change old line j to new line i. */
|
|
162 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
|
|
163 cost = p1->writecost;
|
|
164 if (cost > p1->insertcost)
|
|
165 cost = p1->insertcost;
|
|
166 if (cost > p1->deletecost)
|
|
167 cost = p1->deletecost;
|
|
168 if (old_hash[j] != new_hash[i])
|
|
169 cost += draw_cost[i];
|
|
170 p->writecost = cost;
|
|
171
|
|
172 /* Calculate the cost if we do an insert-line
|
|
173 before outputting this line.
|
|
174 That is, we update through line i-1
|
|
175 based on old lines through j,
|
|
176 do an insert-line on line i,
|
|
177 and then output line i from scratch,
|
|
178 leaving old lines starting from j for reuse below. */
|
|
179 p1 = p - window_size - 1; /* matrix [i-1, j] */
|
|
180 /* No need to think about doing a delete followed
|
|
181 immediately by an insert. It cannot be as good
|
|
182 as not doing either of them. */
|
|
183 if (free_at_end == i)
|
|
184 {
|
|
185 cost = p1->writecost;
|
|
186 cost1 = p1->insertcost;
|
|
187 }
|
|
188 else
|
|
189 {
|
|
190 cost = p1->writecost + first_insert_cost[i];
|
6888
|
191 if ((int) p1->insertcount > i)
|
154
|
192 abort ();
|
|
193 cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
|
|
194 }
|
|
195 p->insertcost = min (cost, cost1) + draw_cost[i] + extra_cost;
|
|
196 p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
|
6888
|
197 if ((int) p->insertcount > i)
|
154
|
198 abort ();
|
|
199
|
|
200 /* Calculate the cost if we do a delete line after
|
|
201 outputting this line.
|
|
202 That is, we update through line i
|
|
203 based on old lines through j-1,
|
|
204 and throw away old line j. */
|
|
205 p1 = p - 1; /* matrix [i, j-1] */
|
|
206 /* No need to think about doing an insert followed
|
|
207 immediately by a delete. */
|
|
208 if (free_at_end == i)
|
|
209 {
|
|
210 cost = p1->writecost;
|
|
211 cost1 = p1->deletecost;
|
|
212 }
|
|
213 else
|
|
214 {
|
|
215 cost = p1->writecost + first_delete_cost[i];
|
|
216 cost1 = p1->deletecost + next_delete_cost[i];
|
|
217 }
|
|
218 p->deletecost = min (cost, cost1);
|
|
219 p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
|
|
220 }
|
|
221 }
|
|
222
|
10262
|
223 /* Perform insert-lines and delete-lines operations on FRAME according
|
|
224 to the costs in MATRIX, using the general scrolling method.
|
6647
|
225 Update the frame's current_glyphs info to record what was done.
|
|
226
|
|
227 WINDOW_SIZE is the number of lines being considered for scrolling
|
|
228 and UNCHANGED_AT_TOP is the vpos of the first line being considered.
|
|
229 These two arguments can specify any contiguous range of lines.
|
|
230
|
|
231 We also shuffle the charstarts vectors for the lines
|
|
232 along with the glyphs; but the results are not quite right,
|
|
233 since we cannot offset them for changes in amount of text
|
|
234 in this line or that line. Luckily it doesn't matter,
|
|
235 since update_frame and update_line will copy in the proper
|
|
236 new charstarts vectors from the frame's desired_glyphs. */
|
154
|
237
|
|
238 static void
|
766
|
239 do_scrolling (frame, matrix, window_size, unchanged_at_top)
|
|
240 FRAME_PTR frame;
|
154
|
241 struct matrix_elt *matrix;
|
|
242 int window_size;
|
|
243 int unchanged_at_top;
|
|
244 {
|
|
245 register struct matrix_elt *p;
|
|
246 register int i, j;
|
766
|
247 register struct frame_glyphs *current_frame;
|
|
248 /* temp_frame->enable[i] means line i has been moved to current_frame. */
|
|
249 register struct frame_glyphs *temp_frame;
|
154
|
250 struct queue { int count, pos; } *queue;
|
|
251 int offset = unchanged_at_top;
|
|
252 int qi = 0;
|
|
253 int window = 0;
|
|
254 register int tem;
|
|
255 int next;
|
|
256
|
766
|
257 queue = (struct queue *) alloca (FRAME_HEIGHT (frame)
|
154
|
258 * sizeof (struct queue));
|
|
259
|
766
|
260 current_frame = FRAME_CURRENT_GLYPHS (frame);
|
|
261 temp_frame = FRAME_TEMP_GLYPHS (frame);
|
154
|
262
|
766
|
263 bcopy (current_frame->glyphs, temp_frame->glyphs,
|
|
264 current_frame->height * sizeof (GLYPH *));
|
6647
|
265 bcopy (current_frame->charstarts, temp_frame->charstarts,
|
|
266 current_frame->height * sizeof (GLYPH *));
|
766
|
267 bcopy (current_frame->used, temp_frame->used,
|
|
268 current_frame->height * sizeof (int));
|
|
269 bcopy (current_frame->highlight, temp_frame->highlight,
|
|
270 current_frame->height * sizeof (char));
|
|
271 bzero (temp_frame->enable, temp_frame->height * sizeof (char));
|
|
272 bcopy (current_frame->bufp, temp_frame->bufp,
|
|
273 current_frame->height * sizeof (int));
|
154
|
274
|
|
275 #ifdef HAVE_X_WINDOWS
|
968
|
276 if (FRAME_X_P (frame))
|
154
|
277 {
|
766
|
278 bcopy (current_frame->top_left_x, temp_frame->top_left_x,
|
|
279 current_frame->height * sizeof (short));
|
|
280 bcopy (current_frame->top_left_y, temp_frame->top_left_y,
|
|
281 current_frame->height * sizeof (short));
|
|
282 bcopy (current_frame->pix_width, temp_frame->pix_width,
|
|
283 current_frame->height * sizeof (short));
|
|
284 bcopy (current_frame->pix_height, temp_frame->pix_height,
|
|
285 current_frame->height * sizeof (short));
|
1410
|
286 bcopy (current_frame->max_ascent, temp_frame->max_ascent,
|
1714
|
287 current_frame->height * sizeof (short));
|
154
|
288 }
|
|
289 #endif
|
|
290
|
|
291 i = j = window_size;
|
|
292
|
|
293 while (i > 0 || j > 0)
|
|
294 {
|
|
295 p = matrix + i * (window_size + 1) + j;
|
|
296 tem = p->insertcost;
|
|
297 if (tem < p->writecost && tem < p->deletecost)
|
|
298 {
|
|
299 /* Insert should be done at vpos i-1, plus maybe some before */
|
|
300 queue[qi].count = p->insertcount;
|
|
301 i -= p->insertcount;
|
|
302 queue[qi++].pos = i + unchanged_at_top;
|
|
303 }
|
|
304 else if (p->deletecost < p->writecost)
|
|
305 {
|
|
306 /* Old line at vpos j-1, and maybe some before it,
|
|
307 should be deleted */
|
|
308 j -= p->deletecount;
|
|
309 if (!window)
|
|
310 {
|
|
311 set_terminal_window (window_size + unchanged_at_top);
|
|
312 window = 1;
|
|
313 }
|
|
314 ins_del_lines (j + unchanged_at_top, - p->deletecount);
|
|
315 }
|
|
316 else
|
|
317 {
|
|
318 /* Best thing done here is no insert or delete */
|
|
319 /* Old line at vpos j-1 ends up at vpos i-1 */
|
766
|
320 current_frame->glyphs[i + offset - 1]
|
|
321 = temp_frame->glyphs[j + offset - 1];
|
6647
|
322 current_frame->charstarts[i + offset - 1]
|
|
323 = temp_frame->charstarts[j + offset - 1];
|
766
|
324 current_frame->used[i + offset - 1]
|
|
325 = temp_frame->used[j + offset - 1];
|
|
326 current_frame->highlight[i + offset - 1]
|
|
327 = temp_frame->highlight[j + offset - 1];
|
154
|
328
|
766
|
329 temp_frame->enable[j + offset - 1] = 1;
|
154
|
330 i--;
|
|
331 j--;
|
|
332 }
|
|
333 }
|
|
334
|
|
335 if (!window && qi)
|
|
336 {
|
|
337 set_terminal_window (window_size + unchanged_at_top);
|
|
338 window = 1;
|
|
339 }
|
|
340
|
|
341 /* Now do all insertions */
|
|
342
|
|
343 next = unchanged_at_top;
|
|
344 for (i = qi - 1; i >= 0; i--)
|
|
345 {
|
|
346 ins_del_lines (queue[i].pos, queue[i].count);
|
|
347
|
|
348 /* Mark the inserted lines as clear,
|
|
349 and put into them the line-contents strings
|
|
350 that were discarded during the deletions.
|
766
|
351 Those are the ones for which temp_frame->enable was not set. */
|
154
|
352 tem = queue[i].pos;
|
320
|
353 for (j = tem + queue[i].count - 1; j >= tem; j--)
|
154
|
354 {
|
766
|
355 current_frame->enable[j] = 0;
|
|
356 while (temp_frame->enable[next])
|
154
|
357 next++;
|
6647
|
358 current_frame->glyphs[j] = temp_frame->glyphs[next];
|
|
359 current_frame->charstarts[j] = temp_frame->charstarts[next++];
|
154
|
360 }
|
|
361 }
|
|
362
|
|
363 if (window)
|
|
364 set_terminal_window (0);
|
|
365 }
|
|
366
|
10262
|
367 /* Determine, in matrix[i,j], the cost of updating the first j
|
|
368 old lines into the first i new lines using the direct
|
|
369 scrolling method. When the old line and the new line have
|
|
370 different hash codes, the calculated cost of updating old
|
|
371 line j into new line i includes the cost of outputting new
|
|
372 line i, and if i != j, the cost of outputting the old line j
|
|
373 is also included, as a penalty for moving the line and then
|
|
374 erasing it. In addition, the cost of updating a sequence of
|
|
375 lines with constant i - j includes the cost of scrolling the
|
|
376 old lines into their new positions, unless i == j. Scrolling
|
|
377 is achieved by setting the screen window to avoid affecting
|
|
378 other lines below, and inserting or deleting lines at the top
|
|
379 of the scrolled region. The cost of scrolling a sequence of
|
|
380 lines includes the fixed cost of specifying a scroll region,
|
|
381 plus a variable cost which can depend upon the number of lines
|
|
382 involved and the distance by which they are scrolled, and an
|
|
383 extra cost to discourage long scrolls.
|
|
384
|
|
385 As reflected in the matrix, an insert or delete does not
|
|
386 correspond directly to the insertion or deletion which is
|
|
387 used in scrolling lines. An insert means that the value of i
|
|
388 has increased without a corresponding increase in the value
|
|
389 of j. A delete means that the value of j has increased
|
|
390 without a corresponding increase in the value of i. A write
|
|
391 means that i and j are both increased by the same amount, and
|
|
392 that the old lines will be moved to their new positions.
|
|
393
|
|
394 An insert following a delete is allowed only if i > j.
|
|
395 A delete following an insert is allowed only if i < j.
|
|
396 These restrictions ensure that the new lines in an insert
|
|
397 will always be blank as an effect of the neighboring writes.
|
|
398 Thus the calculated cost of an insert is simply the cost of
|
|
399 outputting the new line contents. The direct cost of a
|
|
400 delete is zero. Inserts and deletes indirectly affect the
|
|
401 total cost through their influence on subsequent writes. */
|
|
402
|
|
403 /* The vectors draw_cost, old_hash, and new_hash have the same
|
|
404 meanings here as in calculate_scrolling, and old_draw_cost
|
|
405 is the equivalent of draw_cost for the old line contents */
|
|
406
|
|
407 static void
|
|
408 calculate_direct_scrolling (frame, matrix, window_size, lines_below,
|
|
409 draw_cost, old_draw_cost, old_hash, new_hash,
|
|
410 free_at_end)
|
|
411 FRAME_PTR frame;
|
|
412 /* matrix is of size window_size + 1 on each side. */
|
|
413 struct matrix_elt *matrix;
|
|
414 int window_size;
|
|
415 int *draw_cost;
|
|
416 int *old_draw_cost;
|
|
417 int *old_hash;
|
|
418 int *new_hash;
|
|
419 int free_at_end;
|
|
420 {
|
|
421 register int i, j;
|
|
422 int frame_height = FRAME_HEIGHT (frame);
|
|
423 register struct matrix_elt *p, *p1;
|
|
424 register int cost, cost1, delta;
|
|
425
|
|
426 /* first_insert_cost[-I] is the cost of doing the first insert-line
|
|
427 at a position I lines above the bottom line in the scroll window. */
|
|
428 int *first_insert_cost
|
|
429 = &FRAME_INSERT_COST (frame)[frame_height - 1];
|
|
430 int *first_delete_cost
|
|
431 = &FRAME_DELETE_COST (frame)[frame_height - 1];
|
|
432 int *next_insert_cost
|
|
433 = &FRAME_INSERTN_COST (frame)[frame_height - 1];
|
|
434 int *next_delete_cost
|
|
435 = &FRAME_DELETEN_COST (frame)[frame_height - 1];
|
|
436
|
|
437 int scroll_overhead;
|
|
438
|
|
439 /* Discourage long scrolls on fast lines.
|
|
440 Don't scroll nearly a full frame height unless it saves
|
|
441 at least 1/4 second. */
|
|
442 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
|
|
443
|
|
444 if (baud_rate <= 0)
|
|
445 extra_cost = 1;
|
|
446
|
|
447 /* Overhead of setting the scroll window, plus the extra cost
|
|
448 cost of scrolling by a distance of one. The extra cost is
|
|
449 added once for consistency with the cost vectors */
|
|
450 scroll_overhead = scroll_region_cost + extra_cost;
|
|
451
|
|
452 /* initialize the top left corner of the matrix */
|
|
453 matrix->writecost = 0;
|
|
454 matrix->insertcost = INFINITY;
|
|
455 matrix->deletecost = INFINITY;
|
|
456 matrix->writecount = 0;
|
|
457 matrix->insertcount = 0;
|
|
458 matrix->deletecount = 0;
|
|
459
|
|
460 /* initialize the left edge of the matrix */
|
|
461 cost = 0;
|
|
462 for (i = 1; i <= window_size; i++)
|
|
463 {
|
|
464 p = matrix + i * (window_size + 1);
|
|
465 cost += draw_cost[i];
|
|
466 p->insertcost = cost;
|
|
467 p->writecost = INFINITY;
|
|
468 p->deletecost = INFINITY;
|
|
469 p->insertcount = i;
|
|
470 p->writecount = 0;
|
|
471 p->deletecount = 0;
|
|
472 }
|
|
473
|
|
474 /* initialize the top edge of the matrix */
|
|
475 for (j = 1; j <= window_size; j++)
|
|
476 {
|
|
477 matrix[j].deletecost = 0;
|
|
478 matrix[j].writecost = INFINITY;
|
|
479 matrix[j].insertcost = INFINITY;
|
|
480 matrix[j].deletecount = j;
|
|
481 matrix[j].writecount = 0;
|
|
482 matrix[j].insertcount = 0;
|
|
483 }
|
|
484
|
|
485 /* `i' represents the vpos among new frame contents.
|
|
486 `j' represents the vpos among the old frame contents. */
|
|
487 p = matrix + window_size + 2; /* matrix [1, 1] */
|
|
488
|
|
489 for (i = 1; i <= window_size; i++, p++)
|
|
490 for (j = 1; j <= window_size; j++, p++)
|
|
491 {
|
|
492 /* p contains the address of matrix [i, j] */
|
|
493
|
|
494 /* First calculate the cost assuming we do
|
|
495 not insert or delete above this line.
|
|
496 That is, if we update through line i-1
|
|
497 based on old lines through j-1,
|
|
498 and then just change old line j to new line i.
|
|
499
|
|
500 Depending on which choice gives the lower cost,
|
|
501 this usually involves either scrolling a single line
|
|
502 or extending a sequence of scrolled lines, but
|
|
503 when i == j, no scrolling is required. */
|
|
504 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
|
|
505 cost = p1->insertcost;
|
|
506 if (cost > p1->deletecost)
|
|
507 cost = p1->deletecost;
|
|
508 cost1 = p1->writecost;
|
|
509 if (i == j)
|
|
510 {
|
|
511 if (cost > cost1)
|
|
512 {
|
|
513 cost = cost1;
|
|
514 p->writecount = p1->writecount + 1;
|
|
515 }
|
|
516 else
|
|
517 p->writecount = 1;
|
|
518 if (old_hash[j] != new_hash[i])
|
|
519 {
|
|
520 cost += draw_cost[i];
|
|
521 }
|
|
522 }
|
|
523 else
|
|
524 {
|
|
525 if (i > j)
|
|
526 {
|
|
527 delta = i - j;
|
|
528
|
|
529 /* The cost added here for scrolling the first line by
|
|
530 a distance N includes the overhead of setting the
|
|
531 scroll window, the cost of inserting N lines at a
|
|
532 position N lines above the bottom line of the window,
|
|
533 and an extra cost which is proportional to N. */
|
|
534 cost += scroll_overhead + first_insert_cost[-delta] +
|
|
535 (delta-1) * (next_insert_cost[-delta] + extra_cost);
|
|
536
|
|
537 /* In the most general case, the insertion overhead and
|
|
538 the multiply factor can grow linearly as the distance
|
|
539 from the bottom of the window increases. The incremental
|
|
540 cost of scrolling an additional line depends upon the
|
|
541 rate of change of these two parameters. Each of these
|
|
542 growth rates can be determined by a simple difference.
|
|
543 To reduce the cumulative effects of rounding error, we
|
|
544 vary the position at which the difference is computed. */
|
|
545 cost1 += first_insert_cost[-j] - first_insert_cost[1-j] +
|
|
546 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
|
|
547 }
|
|
548 else
|
|
549 {
|
|
550 delta = j - i;
|
|
551 cost += scroll_overhead + first_delete_cost[-delta] +
|
|
552 (delta-1) * (next_delete_cost[-delta] + extra_cost);
|
|
553 cost1 += first_delete_cost[-i] - first_delete_cost[1-i] +
|
|
554 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
|
|
555 }
|
|
556 if (cost1 < cost)
|
|
557 {
|
|
558 cost = cost1;
|
|
559 p->writecount = p1->writecount + 1;
|
|
560 }
|
|
561 else
|
|
562 p->writecount = 1;
|
|
563 if (old_hash[j] != new_hash[i])
|
|
564 {
|
|
565 cost += draw_cost[i] + old_draw_cost[j];
|
|
566 }
|
|
567 }
|
|
568 p->writecost = cost;
|
|
569
|
|
570 /* Calculate the cost if we do an insert-line
|
|
571 before outputting this line.
|
|
572 That is, we update through line i-1
|
|
573 based on old lines through j,
|
|
574 do an insert-line on line i,
|
|
575 and then output line i from scratch,
|
|
576 leaving old lines starting from j for reuse below. */
|
|
577 p1 = p - window_size - 1; /* matrix [i-1, j] */
|
|
578 cost = p1->writecost;
|
|
579 /* If i > j, an insert is allowed after a delete. */
|
|
580 if (i > j && p1->deletecost < cost)
|
|
581 cost = p1->deletecost;
|
|
582 if (p1->insertcost <= cost)
|
|
583 {
|
|
584 cost = p1->insertcost;
|
|
585 p->insertcount = p1->insertcount + 1;
|
|
586 }
|
|
587 else
|
|
588 p->insertcount = 1;
|
|
589 cost += draw_cost[i];
|
|
590 p->insertcost = cost;
|
|
591
|
|
592 /* Calculate the cost if we do a delete line after
|
|
593 outputting this line.
|
|
594 That is, we update through line i
|
|
595 based on old lines through j-1,
|
|
596 and throw away old line j. */
|
|
597 p1 = p - 1; /* matrix [i, j-1] */
|
|
598 cost = p1->writecost;
|
|
599 /* If i < j, a delete is allowed after an insert. */
|
|
600 if (i < j && p1->insertcost < cost)
|
|
601 cost = p1->insertcost;
|
|
602 cost1 = p1->deletecost;
|
|
603 if (p1->deletecost <= cost)
|
|
604 {
|
|
605 cost = p1->deletecost;
|
|
606 p->deletecount = p1->deletecount + 1;
|
|
607 }
|
|
608 else
|
|
609 p->deletecount = 1;
|
|
610 p->deletecost = cost;
|
|
611 }
|
|
612 }
|
|
613
|
|
614 /* Perform insert-lines and delete-lines operations on FRAME according
|
|
615 to the costs in MATRIX, using the direct scrolling method.
|
|
616 Update the frame's current_glyphs info to record what was done.
|
|
617
|
|
618 WINDOW_SIZE is the number of lines being considered for scrolling
|
|
619 and UNCHANGED_AT_TOP is the vpos of the first line being considered.
|
|
620 These two arguments can specify any contiguous range of lines.
|
|
621
|
|
622 We also shuffle the charstarts vectors for the lines
|
|
623 along with the glyphs; but the results are not quite right,
|
|
624 since we cannot offset them for changes in amount of text
|
|
625 in this line or that line. Luckily it doesn't matter,
|
|
626 since update_frame and update_line will copy in the proper
|
|
627 new charstarts vectors from the frame's desired_glyphs.
|
|
628
|
|
629 In the direct scrolling method, a new scroll window is selected
|
|
630 before each insertion or deletion, so that groups of lines can be
|
|
631 scrolled directly to their final vertical positions. This method
|
|
632 is described in more detail in calculate_direct_scrolling,
|
|
633 where the cost matrix for this approach is constructed. */
|
|
634
|
|
635 static void
|
|
636 do_direct_scrolling (frame, matrix, window_size, unchanged_at_top)
|
|
637 FRAME_PTR frame;
|
|
638 struct matrix_elt *matrix;
|
|
639 int window_size;
|
|
640 int unchanged_at_top;
|
|
641 {
|
|
642 register struct matrix_elt *p;
|
|
643 register int i, j;
|
|
644 register struct frame_glyphs *current_frame;
|
|
645 /* temp_frame->enable[i] means line i has been moved to current_frame. */
|
|
646 register struct frame_glyphs *temp_frame;
|
|
647 struct alt_queue { int count, pos, window; } *queue;
|
|
648 int offset = unchanged_at_top;
|
|
649 int qi = 0;
|
|
650 int window = 0;
|
|
651 register int tem;
|
|
652 int next;
|
|
653
|
|
654 /* A nonzero value of write_follows indicates that a write has been
|
|
655 selected, allowing either an insert or a delete to be selected next.
|
|
656 When write_follows is zero, a delete cannot be selected unless j < i,
|
|
657 and an insert cannot be selected unless i < j. This corresponds to
|
|
658 a similar restriction (with the ordering reversed) in
|
|
659 calculate_direct_scrolling, which is intended to ensure that lines
|
|
660 marked as inserted will be blank. */
|
|
661 int write_follows = 1;
|
|
662
|
|
663 queue = (struct alt_queue *) alloca (FRAME_HEIGHT (frame)
|
|
664 * sizeof (struct alt_queue));
|
|
665
|
|
666 current_frame = FRAME_CURRENT_GLYPHS (frame);
|
|
667 temp_frame = FRAME_TEMP_GLYPHS (frame);
|
|
668
|
|
669 bcopy (current_frame->glyphs, temp_frame->glyphs,
|
|
670 current_frame->height * sizeof (GLYPH *));
|
|
671 bcopy (current_frame->charstarts, temp_frame->charstarts,
|
|
672 current_frame->height * sizeof (GLYPH *));
|
|
673 bcopy (current_frame->used, temp_frame->used,
|
|
674 current_frame->height * sizeof (int));
|
|
675 bcopy (current_frame->highlight, temp_frame->highlight,
|
|
676 current_frame->height * sizeof (char));
|
|
677 bzero (temp_frame->enable, temp_frame->height * sizeof (char));
|
|
678 bcopy (current_frame->bufp, temp_frame->bufp,
|
|
679 current_frame->height * sizeof (int));
|
|
680
|
|
681 #ifdef HAVE_X_WINDOWS
|
|
682 if (FRAME_X_P (frame))
|
|
683 {
|
|
684 bcopy (current_frame->top_left_x, temp_frame->top_left_x,
|
|
685 current_frame->height * sizeof (short));
|
|
686 bcopy (current_frame->top_left_y, temp_frame->top_left_y,
|
|
687 current_frame->height * sizeof (short));
|
|
688 bcopy (current_frame->pix_width, temp_frame->pix_width,
|
|
689 current_frame->height * sizeof (short));
|
|
690 bcopy (current_frame->pix_height, temp_frame->pix_height,
|
|
691 current_frame->height * sizeof (short));
|
|
692 bcopy (current_frame->max_ascent, temp_frame->max_ascent,
|
|
693 current_frame->height * sizeof (short));
|
|
694 }
|
|
695 #endif
|
|
696
|
|
697 i = j = window_size;
|
|
698
|
|
699 while (i > 0 || j > 0)
|
|
700 {
|
|
701 p = matrix + i * (window_size + 1) + j;
|
|
702 tem = p->insertcost;
|
|
703 if (tem < p->writecost && tem < p->deletecost
|
|
704 && (write_follows || i < j))
|
|
705 {
|
|
706 /* Insert should be done at vpos i-1, plus maybe some before.
|
|
707 Setting count to 0 in the queue marks this as an insert. */
|
|
708 write_follows = 0;
|
|
709 queue[qi].window = i + unchanged_at_top;
|
|
710 queue[qi].count = 0;
|
|
711 i -= p->insertcount;
|
|
712 queue[qi++].pos = i + unchanged_at_top;
|
|
713 }
|
|
714 else if (p->deletecost < p->writecost && (write_follows || i > j))
|
|
715 {
|
|
716 /* Delete should be done at vpos j-1, plus maybe some before. */
|
|
717 write_follows = 0;
|
|
718 j -= p->deletecount;
|
|
719 }
|
|
720 else
|
|
721 {
|
|
722 /* One or more lines should be retained. */
|
|
723 write_follows = 1;
|
|
724 tem = p->writecount;
|
|
725 if (i > j)
|
|
726 {
|
|
727 /* Immediately scroll a group of lines downward */
|
|
728 set_terminal_window (i + unchanged_at_top);
|
|
729 window = 1;
|
|
730 ins_del_lines (j - tem + unchanged_at_top, i - j);
|
|
731 }
|
|
732 else if (i < j)
|
|
733 {
|
|
734 /* Queue the upward scrolling of a group of lines */
|
|
735 queue[qi].pos = i - tem + unchanged_at_top;
|
|
736 queue[qi].window = j + unchanged_at_top;
|
|
737 queue[qi++].count = i - j;
|
|
738 }
|
|
739
|
|
740 /* Now copy the line-contents strings */
|
|
741 while (tem > 0)
|
|
742 {
|
|
743 i--;
|
|
744 j--;
|
|
745 tem--;
|
|
746 current_frame->glyphs[i + offset]
|
|
747 = temp_frame->glyphs[j + offset];
|
|
748 current_frame->charstarts[i + offset]
|
|
749 = temp_frame->charstarts[j + offset];
|
|
750 current_frame->used[i + offset]
|
|
751 = temp_frame->used[j + offset];
|
|
752 current_frame->highlight[i + offset]
|
|
753 = temp_frame->highlight[j + offset];
|
|
754
|
|
755 temp_frame->enable[j + offset] = 1;
|
|
756 }
|
|
757 }
|
|
758 }
|
|
759
|
|
760 /* Now do all the upward scrolling, and copy the line-contents
|
|
761 strings for the blank lines of the recorded inserts. */
|
|
762
|
|
763 next = unchanged_at_top;
|
|
764 for (i = qi - 1; i >= 0; i--)
|
|
765 {
|
|
766 if (queue[i].count)
|
|
767 {
|
|
768 set_terminal_window (queue[i].window);
|
|
769 window = 1;
|
|
770 ins_del_lines (queue[i].pos, queue[i].count);
|
|
771 }
|
|
772 else
|
|
773 {
|
|
774 /* Mark the inserted lines as clear,
|
|
775 and put into them the line-contents strings
|
|
776 that were discarded during the deletions.
|
|
777 Those are the ones for which temp_frame->enable was not set. */
|
|
778 tem = queue[i].pos;
|
|
779 for (j = queue[i].window - 1; j >= tem; j--)
|
|
780 {
|
|
781 current_frame->enable[j] = 0;
|
|
782 while (temp_frame->enable[next])
|
|
783 next++;
|
|
784 current_frame->glyphs[j] = temp_frame->glyphs[next];
|
|
785 current_frame->charstarts[j] = temp_frame->charstarts[next++];
|
|
786 }
|
|
787 }
|
|
788 }
|
|
789
|
|
790 if (window)
|
|
791 set_terminal_window (0);
|
|
792 }
|
|
793
|
154
|
794 void
|
766
|
795 scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
|
10262
|
796 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
|
766
|
797 FRAME_PTR frame;
|
154
|
798 int window_size, unchanged_at_top, unchanged_at_bottom;
|
|
799 int *draw_cost;
|
10262
|
800 int *old_draw_cost;
|
154
|
801 int *old_hash;
|
|
802 int *new_hash;
|
|
803 int free_at_end;
|
|
804 {
|
|
805 struct matrix_elt *matrix;
|
|
806 matrix = ((struct matrix_elt *)
|
|
807 alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
|
|
808
|
10262
|
809 if (scroll_region_ok)
|
|
810 {
|
|
811 calculate_direct_scrolling (frame, matrix, window_size,
|
|
812 unchanged_at_bottom,
|
|
813 draw_cost, old_draw_cost,
|
|
814 old_hash, new_hash, free_at_end);
|
|
815 do_direct_scrolling (frame, matrix, window_size, unchanged_at_top);
|
|
816 }
|
|
817 else
|
|
818 {
|
|
819 calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
|
|
820 draw_cost, old_hash, new_hash,
|
|
821 free_at_end);
|
|
822 do_scrolling (frame, matrix, window_size, unchanged_at_top);
|
|
823 }
|
154
|
824 }
|
|
825
|
766
|
826 /* Return number of lines in common between current and desired frame contents
|
154
|
827 described to us only as vectors of hash codes OLDHASH and NEWHASH.
|
|
828 Consider only vpos range START to END (not including END).
|
|
829 Ignore short lines on the assumption that
|
|
830 avoiding redrawing such a line will have little weight. */
|
|
831
|
|
832 int
|
|
833 scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
|
|
834 int start, end;
|
|
835 int *oldhash, *newhash, *cost;
|
|
836 {
|
|
837 struct { int hash; int count; } lines[01000];
|
|
838 register int i, h;
|
|
839 register int matchcount = 0;
|
|
840 int avg_length = 0;
|
|
841 int threshold;
|
|
842
|
|
843 /* Compute a threshold which is 1/4 of average length of these lines. */
|
|
844
|
|
845 for (i = start; i < end; i++)
|
|
846 avg_length += cost[i];
|
|
847
|
|
848 avg_length /= end - start;
|
|
849 threshold = avg_length / 4;
|
|
850
|
|
851 bzero (lines, sizeof lines);
|
|
852
|
|
853 /* Put new lines' hash codes in hash table.
|
|
854 Ignore lines shorter than the threshold.
|
|
855 Thus, if the lines that are in common
|
|
856 are mainly the ones that are short,
|
|
857 they won't count. */
|
|
858 for (i = start; i < end; i++)
|
|
859 {
|
|
860 if (cost[i] > threshold)
|
|
861 {
|
|
862 h = newhash[i] & 0777;
|
|
863 lines[h].hash = newhash[i];
|
|
864 lines[h].count++;
|
|
865 }
|
|
866 }
|
|
867
|
|
868 /* Look up old line hash codes in the hash table.
|
|
869 Count number of matches between old lines and new. */
|
|
870
|
|
871 for (i = start; i < end; i++)
|
|
872 {
|
|
873 h = oldhash[i] & 0777;
|
|
874 if (oldhash[i] == lines[h].hash)
|
|
875 {
|
|
876 matchcount++;
|
|
877 if (--lines[h].count == 0)
|
|
878 lines[h].hash = 0;
|
|
879 }
|
|
880 }
|
|
881
|
|
882 return matchcount;
|
|
883 }
|
|
884
|
|
885 /* Return a measure of the cost of moving the lines
|
|
886 starting with vpos FROM, up to but not including vpos TO,
|
|
887 down by AMOUNT lines (AMOUNT may be negative).
|
|
888 These are the same arguments that might be given to
|
766
|
889 scroll_frame_lines to perform this scrolling. */
|
154
|
890
|
766
|
891 scroll_cost (frame, from, to, amount)
|
|
892 FRAME_PTR frame;
|
154
|
893 int from, to, amount;
|
|
894 {
|
766
|
895 /* Compute how many lines, at bottom of frame,
|
154
|
896 will not be involved in actual motion. */
|
|
897 int limit = to;
|
|
898 int offset;
|
766
|
899 int height = FRAME_HEIGHT (frame);
|
154
|
900
|
421
|
901 if (amount == 0)
|
|
902 return 0;
|
|
903
|
154
|
904 if (! scroll_region_ok)
|
|
905 limit = height;
|
421
|
906 else if (amount > 0)
|
|
907 limit += amount;
|
154
|
908
|
|
909 if (amount < 0)
|
|
910 {
|
|
911 int temp = to;
|
|
912 to = from + amount;
|
|
913 from = temp + amount;
|
|
914 amount = - amount;
|
|
915 }
|
|
916
|
|
917 offset = height - limit;
|
|
918
|
|
919 return
|
766
|
920 (FRAME_INSERT_COST (frame)[offset + from]
|
|
921 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
|
10109
869e177ca872
(scroll_cost): FRAME_DELETE_COST and FRAME_DELETEN_COSTS were confused. Fixed.
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
922 + FRAME_DELETE_COST (frame)[offset + to]
|
869e177ca872
(scroll_cost): FRAME_DELETE_COST and FRAME_DELETEN_COSTS were confused. Fixed.
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
923 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
|
154
|
924 }
|
|
925
|
|
926 /* Calculate the line insertion/deletion
|
|
927 overhead and multiply factor values */
|
|
928
|
|
929 static void
|
766
|
930 line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
|
|
931 FRAME_PTR frame;
|
154
|
932 int ov1, ovn;
|
|
933 int pf1, pfn;
|
|
934 register int *ov, *mf;
|
|
935 {
|
|
936 register int i;
|
766
|
937 register int frame_height = FRAME_HEIGHT (frame);
|
154
|
938 register int insert_overhead = ov1 * 10;
|
|
939 register int next_insert_cost = ovn * 10;
|
|
940
|
766
|
941 for (i = frame_height-1; i >= 0; i--)
|
154
|
942 {
|
529
|
943 mf[i] = next_insert_cost / 10;
|
154
|
944 next_insert_cost += pfn;
|
529
|
945 ov[i] = (insert_overhead + next_insert_cost) / 10;
|
154
|
946 insert_overhead += pf1;
|
|
947 }
|
|
948 }
|
|
949
|
|
950 static void
|
766
|
951 ins_del_costs (frame,
|
154
|
952 one_line_string, multi_string,
|
|
953 setup_string, cleanup_string,
|
|
954 costvec, ncostvec, coefficient)
|
766
|
955 FRAME_PTR frame;
|
154
|
956 char *one_line_string, *multi_string;
|
|
957 char *setup_string, *cleanup_string;
|
|
958 int *costvec, *ncostvec;
|
|
959 int coefficient;
|
|
960 {
|
|
961 if (multi_string)
|
766
|
962 line_ins_del (frame,
|
154
|
963 string_cost (multi_string) * coefficient,
|
|
964 per_line_cost (multi_string) * coefficient,
|
|
965 0, 0, costvec, ncostvec);
|
|
966 else if (one_line_string)
|
766
|
967 line_ins_del (frame,
|
154
|
968 string_cost (setup_string) + string_cost (cleanup_string), 0,
|
|
969 string_cost (one_line_string),
|
|
970 per_line_cost (one_line_string),
|
|
971 costvec, ncostvec);
|
|
972 else
|
766
|
973 line_ins_del (frame,
|
154
|
974 9999, 0, 9999, 0,
|
|
975 costvec, ncostvec);
|
|
976 }
|
|
977
|
|
978 /* Calculate the insert and delete line costs.
|
|
979 Note that this is done even when running with a window system
|
|
980 because we want to know how long scrolling takes (and avoid it).
|
766
|
981 This must be redone whenever the frame height changes.
|
154
|
982
|
|
983 We keep the ID costs in a precomputed array based on the position
|
|
984 at which the I or D is performed. Also, there are two kinds of ID
|
|
985 costs: the "once-only" and the "repeated". This is to handle both
|
|
986 those terminals that are able to insert N lines at a time (once-
|
|
987 only) and those that must repeatedly insert one line.
|
|
988
|
|
989 The cost to insert N lines at line L is
|
766
|
990 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
|
|
991 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
|
154
|
992
|
|
993 ILov represents the basic insert line overhead. ILpf is the padding
|
|
994 required to allow the terminal time to move a line: insertion at line
|
766
|
995 L changes (frame_height + 1 - L) lines.
|
154
|
996
|
|
997 The first bracketed expression above is the overhead; the second is
|
|
998 the multiply factor. Both are dependent only on the position at
|
|
999 which the insert is performed. We store the overhead in
|
766
|
1000 FRAME_INSERT_COST (frame) and the multiply factor in
|
|
1001 FRAME_INSERTN_COST (frame). Note however that any insertion
|
154
|
1002 must include at least one multiply factor. Rather than compute this
|
766
|
1003 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
|
|
1004 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
|
154
|
1005 This is reasonable because of the particular algorithm used in calcM.
|
|
1006
|
|
1007 Deletion is essentially the same as insertion.
|
|
1008 */
|
|
1009
|
766
|
1010 do_line_insertion_deletion_costs (frame,
|
154
|
1011 ins_line_string, multi_ins_string,
|
|
1012 del_line_string, multi_del_string,
|
|
1013 setup_string, cleanup_string, coefficient)
|
766
|
1014 FRAME_PTR frame;
|
154
|
1015 char *ins_line_string, *multi_ins_string;
|
|
1016 char *del_line_string, *multi_del_string;
|
|
1017 char *setup_string, *cleanup_string;
|
|
1018 int coefficient;
|
|
1019 {
|
766
|
1020 if (FRAME_INSERT_COST (frame) != 0)
|
154
|
1021 {
|
766
|
1022 FRAME_INSERT_COST (frame) =
|
|
1023 (int *) xrealloc (FRAME_INSERT_COST (frame),
|
|
1024 FRAME_HEIGHT (frame) * sizeof (int));
|
|
1025 FRAME_DELETEN_COST (frame) =
|
|
1026 (int *) xrealloc (FRAME_DELETEN_COST (frame),
|
|
1027 FRAME_HEIGHT (frame) * sizeof (int));
|
|
1028 FRAME_INSERTN_COST (frame) =
|
|
1029 (int *) xrealloc (FRAME_INSERTN_COST (frame),
|
|
1030 FRAME_HEIGHT (frame) * sizeof (int));
|
|
1031 FRAME_DELETE_COST (frame) =
|
|
1032 (int *) xrealloc (FRAME_DELETE_COST (frame),
|
|
1033 FRAME_HEIGHT (frame) * sizeof (int));
|
154
|
1034 }
|
|
1035 else
|
|
1036 {
|
766
|
1037 FRAME_INSERT_COST (frame) =
|
|
1038 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
|
|
1039 FRAME_DELETEN_COST (frame) =
|
|
1040 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
|
|
1041 FRAME_INSERTN_COST (frame) =
|
|
1042 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
|
|
1043 FRAME_DELETE_COST (frame) =
|
|
1044 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
|
154
|
1045 }
|
|
1046
|
766
|
1047 ins_del_costs (frame,
|
154
|
1048 ins_line_string, multi_ins_string,
|
|
1049 setup_string, cleanup_string,
|
766
|
1050 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
|
154
|
1051 coefficient);
|
766
|
1052 ins_del_costs (frame,
|
154
|
1053 del_line_string, multi_del_string,
|
|
1054 setup_string, cleanup_string,
|
9576
|
1055 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
|
154
|
1056 coefficient);
|
|
1057 }
|