xref: /reactos/dll/win32/riched20/wrap.c (revision cc7cf826)
1 /*
2  * RichEdit - Paragraph wrapping. Don't try to understand it. You've been
3  * warned !
4  *
5  * Copyright 2004 by Krzysztof Foltman
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 
23 #include "editor.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
26 
27 /*
28  * Unsolved problems:
29  *
30  * - center and right align in WordPad omits all spaces at the start, we don't
31  * - objects/images are not handled yet
32  * - no tabs
33  */
34 
35 typedef struct tagME_WrapContext
36 {
37   ME_Style *style;
38   ME_Context *context;
39   int nLeftMargin, nRightMargin;
40   int nFirstMargin;   /* Offset to first line's text, always to the text itself even if a para number is present */
41   int nParaNumOffset; /* Offset to the para number */
42   int nAvailWidth;    /* Width avail for text to wrap into.  Does not include any para number text */
43   int nRow;
44   POINT pt;
45   BOOL bOverflown, bWordWrap;
46   ME_DisplayItem *pPara;
47   ME_DisplayItem *pRowStart;
48 
49   ME_DisplayItem *pLastSplittableRun;
50 } ME_WrapContext;
51 
52 static BOOL get_run_glyph_buffers( ME_Run *run )
53 {
54     heap_free( run->glyphs );
55     run->glyphs = heap_alloc( run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int) + sizeof(GOFFSET)) );
56     if (!run->glyphs) return FALSE;
57 
58     run->vis_attrs = (SCRIPT_VISATTR*)((char*)run->glyphs + run->max_glyphs * sizeof(WORD));
59     run->advances = (int*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR)));
60     run->offsets = (GOFFSET*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int)));
61 
62     return TRUE;
63 }
64 
65 static HRESULT shape_run( ME_Context *c, ME_Run *run )
66 {
67     HRESULT hr;
68     int i;
69 
70     if (!run->glyphs)
71     {
72         run->max_glyphs = 1.5 * run->len + 16; /* This is suggested in the uniscribe documentation */
73         run->max_glyphs = (run->max_glyphs + 7) & ~7; /* Keep alignment simple */
74         get_run_glyph_buffers( run );
75     }
76 
77     if (run->max_clusters < run->len)
78     {
79         heap_free( run->clusters );
80         run->max_clusters = run->len * 2;
81         run->clusters = heap_alloc( run->max_clusters * sizeof(WORD) );
82     }
83 
84     select_style( c, run->style );
85     while (1)
86     {
87         hr = ScriptShape( c->hDC, &run->style->script_cache, get_text( run, 0 ), run->len, run->max_glyphs,
88                           &run->script_analysis, run->glyphs, run->clusters, run->vis_attrs, &run->num_glyphs );
89         if (hr != E_OUTOFMEMORY) break;
90         if (run->max_glyphs > 10 * run->len) break; /* something has clearly gone wrong */
91         run->max_glyphs *= 2;
92         get_run_glyph_buffers( run );
93     }
94 
95     if (SUCCEEDED(hr))
96         hr = ScriptPlace( c->hDC, &run->style->script_cache, run->glyphs, run->num_glyphs, run->vis_attrs,
97                           &run->script_analysis, run->advances, run->offsets, NULL );
98 
99     if (SUCCEEDED(hr))
100     {
101         for (i = 0, run->nWidth = 0; i < run->num_glyphs; i++)
102             run->nWidth += run->advances[i];
103     }
104 
105     return hr;
106 }
107 
108 /******************************************************************************
109  * calc_run_extent
110  *
111  * Updates the size of the run (fills width, ascent and descent). The height
112  * is calculated based on whole row's ascent and descent anyway, so no need
113  * to use it here.
114  */
115 static void calc_run_extent(ME_Context *c, const ME_Paragraph *para, int startx, ME_Run *run)
116 {
117     if (run->nFlags & MERF_HIDDEN) run->nWidth = 0;
118     else
119     {
120         SIZE size = ME_GetRunSizeCommon( c, para, run, run->len, startx, &run->nAscent, &run->nDescent );
121         run->nWidth = size.cx;
122     }
123 }
124 
125 /******************************************************************************
126  * split_run_extents
127  *
128  * Splits a run into two in a given place. It also updates the screen position
129  * and size (extent) of the newly generated runs.
130  */
131 static ME_DisplayItem *split_run_extents(ME_WrapContext *wc, ME_DisplayItem *item, int nVChar)
132 {
133   ME_TextEditor *editor = wc->context->editor;
134   ME_Run *run, *run2;
135   ME_Paragraph *para = &wc->pPara->member.para;
136   ME_Cursor cursor = {wc->pPara, item, nVChar};
137 
138   assert(item->member.run.nCharOfs != -1);
139   ME_CheckCharOffsets(editor);
140 
141   run = &item->member.run;
142 
143   TRACE("Before split: %s(%d, %d)\n", debugstr_run( run ),
144         run->pt.x, run->pt.y);
145 
146   ME_SplitRunSimple(editor, &cursor);
147 
148   run2 = &cursor.pRun->member.run;
149   run2->script_analysis = run->script_analysis;
150 
151   shape_run( wc->context, run );
152   shape_run( wc->context, run2 );
153   calc_run_extent(wc->context, para, wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run);
154 
155   run2->pt.x = run->pt.x+run->nWidth;
156   run2->pt.y = run->pt.y;
157 
158   ME_CheckCharOffsets(editor);
159 
160   TRACE("After split: %s(%d, %d), %s(%d, %d)\n",
161         debugstr_run( run ), run->pt.x, run->pt.y,
162         debugstr_run( run2 ), run2->pt.x, run2->pt.y);
163 
164   return cursor.pRun;
165 }
166 
167 /******************************************************************************
168  * find_split_point
169  *
170  * Returns a character position to split inside the run given a run-relative
171  * pixel horizontal position. This version rounds left (ie. if the second
172  * character is at pixel position 8, then for cx=0..7 it returns 0).
173  */
174 static int find_split_point( ME_Context *c, int cx, ME_Run *run )
175 {
176     if (!run->len || cx <= 0) return 0;
177     return ME_CharFromPointContext( c, cx, run, FALSE, FALSE );
178 }
179 
180 static ME_DisplayItem *ME_MakeRow(int height, int baseline, int width)
181 {
182   ME_DisplayItem *item = ME_MakeDI(diStartRow);
183 
184   item->member.row.nHeight = height;
185   item->member.row.nBaseline = baseline;
186   item->member.row.nWidth = width;
187   return item;
188 }
189 
190 static void ME_BeginRow(ME_WrapContext *wc)
191 {
192   PARAFORMAT2 *pFmt;
193   ME_DisplayItem *para = wc->pPara;
194 
195   pFmt = &para->member.para.fmt;
196   wc->pRowStart = NULL;
197   wc->bOverflown = FALSE;
198   wc->pLastSplittableRun = NULL;
199   wc->bWordWrap = wc->context->editor->bWordWrap;
200   if (para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND)) {
201     wc->nAvailWidth = 0;
202     wc->bWordWrap = FALSE;
203     if (para->member.para.nFlags & MEPF_ROWEND)
204     {
205       ME_Cell *cell = &ME_FindItemBack(para, diCell)->member.cell;
206       cell->nWidth = 0;
207     }
208   } else if (para->member.para.pCell) {
209     ME_Cell *cell = &para->member.para.pCell->member.cell;
210     int width;
211 
212     width = cell->nRightBoundary;
213     if (cell->prev_cell)
214       width -= cell->prev_cell->member.cell.nRightBoundary;
215     if (!cell->prev_cell)
216     {
217       int rowIndent = ME_GetTableRowEnd(para)->member.para.fmt.dxStartIndent;
218       width -= rowIndent;
219     }
220     cell->nWidth = max(ME_twips2pointsX(wc->context, width), 0);
221 
222     wc->nAvailWidth = cell->nWidth
223         - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
224     wc->bWordWrap = TRUE;
225   } else {
226     wc->nAvailWidth = wc->context->nAvailWidth
227         - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
228   }
229   wc->pt.x = wc->context->pt.x;
230   if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
231       pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
232     /* Shift the text down because of the border. */
233     wc->pt.y++;
234 }
235 
236 static void layout_row( ME_DisplayItem *start, const ME_DisplayItem *end )
237 {
238     ME_DisplayItem *p;
239     int i, num_runs = 0;
240     int buf[16 * 5]; /* 5 arrays - 4 of int & 1 of BYTE, alloc space for 5 of ints */
241     int *vis_to_log = buf, *log_to_vis, *widths, *pos;
242     BYTE *levels;
243     BOOL found_black = FALSE;
244 
245     for (p = end->prev; p != start->prev; p = p->prev)
246     {
247         if (p->type == diRun)
248         {
249             if (!found_black) found_black = !(p->member.run.nFlags & (MERF_WHITESPACE | MERF_ENDPARA));
250             if (found_black) num_runs++;
251         }
252     }
253 
254     TRACE("%d runs\n", num_runs);
255     if (!num_runs) return;
256 
257     if (num_runs > ARRAY_SIZE( buf ) / 5)
258         vis_to_log = heap_alloc( num_runs * sizeof(int) * 5 );
259 
260     log_to_vis = vis_to_log + num_runs;
261     widths = vis_to_log + 2 * num_runs;
262     pos = vis_to_log + 3 * num_runs;
263     levels = (BYTE*)(vis_to_log + 4 * num_runs);
264 
265     for (i = 0, p = start; i < num_runs; p = p->next)
266     {
267         if (p->type == diRun)
268         {
269             levels[i] = p->member.run.script_analysis.s.uBidiLevel;
270             widths[i] = p->member.run.nWidth;
271             TRACE( "%d: level %d width %d\n", i, levels[i], widths[i] );
272             i++;
273         }
274     }
275 
276     ScriptLayout( num_runs, levels, vis_to_log, log_to_vis );
277 
278     pos[0] = start->member.run.para->pt.x;
279     for (i = 1; i < num_runs; i++)
280         pos[i] = pos[i - 1] + widths[ vis_to_log[ i - 1 ] ];
281 
282     for (i = 0, p = start; i < num_runs; p = p->next)
283     {
284         if (p->type == diRun)
285         {
286             p->member.run.pt.x = pos[ log_to_vis[ i ] ];
287             TRACE( "%d: x = %d\n", i, p->member.run.pt.x );
288             i++;
289         }
290     }
291 
292     if (vis_to_log != buf) heap_free( vis_to_log );
293 }
294 
295 static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
296 {
297   ME_DisplayItem *p, *row;
298   ME_Paragraph *para = &wc->pPara->member.para;
299   BOOL bSkippingSpaces = TRUE;
300   int ascent = 0, descent = 0, width=0, shift = 0, align = 0;
301 
302   /* Include height of para numbering label */
303   if (wc->nRow == 0 && para->fmt.wNumbering)
304   {
305       ascent = para->para_num.style->tm.tmAscent;
306       descent = para->para_num.style->tm.tmDescent;
307   }
308 
309   for (p = pEnd->prev; p!=wc->pRowStart->prev; p = p->prev)
310   {
311       /* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */
312       if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */
313         if (p->member.run.nAscent>ascent)
314           ascent = p->member.run.nAscent;
315         if (p->member.run.nDescent>descent)
316           descent = p->member.run.nDescent;
317         if (bSkippingSpaces)
318         {
319           /* Exclude space characters from run width.
320            * Other whitespace or delimiters are not treated this way. */
321           int len = p->member.run.len;
322           WCHAR *text = get_text( &p->member.run, len - 1 );
323 
324           assert (len);
325           if (~p->member.run.nFlags & MERF_GRAPHICS)
326             while (len && *(text--) == ' ')
327               len--;
328           if (len)
329           {
330               if (len == p->member.run.len)
331                   width += p->member.run.nWidth;
332               else
333                   width += ME_PointFromCharContext( wc->context, &p->member.run, len, FALSE );
334           }
335           bSkippingSpaces = !len;
336         } else if (!(p->member.run.nFlags & MERF_ENDPARA))
337           width += p->member.run.nWidth;
338       }
339   }
340 
341   para->nWidth = max(para->nWidth, width);
342   row = ME_MakeRow(ascent+descent, ascent, width);
343   if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
344       (para->fmt.dwMask & PFM_TABLE) && (para->fmt.wEffects & PFE_TABLE))
345   {
346     /* The text was shifted down in ME_BeginRow so move the wrap context
347      * back to where it should be. */
348     wc->pt.y--;
349     /* The height of the row is increased by the borders. */
350     row->member.row.nHeight += 2;
351   }
352   row->member.row.pt = wc->pt;
353   row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin);
354   row->member.row.nRMargin = wc->nRightMargin;
355   assert(para->fmt.dwMask & PFM_ALIGNMENT);
356   align = para->fmt.wAlignment;
357   if (align == PFA_CENTER)
358     shift = max((wc->nAvailWidth-width)/2, 0);
359   if (align == PFA_RIGHT)
360     shift = max(wc->nAvailWidth-width, 0);
361 
362   if (para->nFlags & MEPF_COMPLEX) layout_row( wc->pRowStart, pEnd );
363 
364   row->member.row.pt.x = row->member.row.nLMargin + shift;
365   for (p = wc->pRowStart; p!=pEnd; p = p->next)
366   {
367     if (p->type==diRun) { /* FIXME add more run types */
368       p->member.run.pt.x += row->member.row.nLMargin+shift;
369     }
370   }
371 
372   if (wc->nRow == 0 && para->fmt.wNumbering)
373   {
374     para->para_num.pt.x = wc->nParaNumOffset + shift;
375     para->para_num.pt.y = wc->pt.y + row->member.row.nBaseline;
376   }
377 
378   ME_InsertBefore(wc->pRowStart, row);
379   wc->nRow++;
380   wc->pt.y += row->member.row.nHeight;
381   ME_BeginRow(wc);
382 }
383 
384 static void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p)
385 {
386   ME_DisplayItem *para = wc->pPara;
387   PARAFORMAT2 *pFmt = &para->member.para.fmt;
388   if (wc->pRowStart)
389     ME_InsertRowStart(wc, p);
390   if (wc->context->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
391       pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
392   {
393     /* ME_BeginRow was called an extra time for the paragraph, and it shifts the
394      * text down by one pixel for the border, so fix up the wrap context. */
395     wc->pt.y--;
396   }
397 
398   /*
399   p = para->next;
400   while(p) {
401     if (p->type == diParagraph || p->type == diTextEnd)
402       return;
403     if (p->type == diRun)
404     {
405       ME_Run *run = &p->member.run;
406       TRACE("%s - (%d, %d)\n", debugstr_run(run), run->pt.x, run->pt.y);
407     }
408     p = p->next;
409   }
410   */
411 }
412 
413 static void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p)
414 {
415   /* FIXME compose style (out of character and paragraph styles) here */
416 
417   ME_UpdateRunFlags(wc->context->editor, &p->member.run);
418 
419   calc_run_extent(wc->context, &wc->pPara->member.para,
420                   wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, &p->member.run);
421 }
422 
423 
424 static int find_non_whitespace(const WCHAR *s, int len, int start)
425 {
426   int i;
427   for (i = start; i < len && ME_IsWSpace( s[i] ); i++)
428     ;
429 
430   return i;
431 }
432 
433 /* note: these two really return the first matching offset (starting from EOS)+1
434  * in other words, an offset of the first trailing white/black */
435 
436 /* note: returns offset of the first trailing whitespace */
437 static int reverse_find_non_whitespace(const WCHAR *s, int start)
438 {
439   int i;
440   for (i = start; i > 0 && ME_IsWSpace( s[i - 1] ); i--)
441     ;
442 
443   return i;
444 }
445 
446 /* note: returns offset of the first trailing nonwhitespace */
447 static int reverse_find_whitespace(const WCHAR *s, int start)
448 {
449   int i;
450   for (i = start; i > 0 && !ME_IsWSpace( s[i - 1] ); i--)
451     ;
452 
453   return i;
454 }
455 
456 static ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i)
457 {
458   ME_DisplayItem *pp, *piter = p;
459   int j;
460   if (!i)
461     return NULL;
462   j = reverse_find_non_whitespace( get_text( &p->member.run, 0 ), i);
463   if (j>0) {
464     pp = split_run_extents(wc, piter, j);
465     wc->pt.x += piter->member.run.nWidth;
466     return pp;
467   }
468   else
469   {
470     pp = piter;
471     /* omit all spaces before split point */
472     while(piter != wc->pRowStart)
473     {
474       piter = ME_FindItemBack(piter, diRun);
475       if (piter->member.run.nFlags & MERF_WHITESPACE)
476       {
477         pp = piter;
478         continue;
479       }
480       if (piter->member.run.nFlags & MERF_ENDWHITE)
481       {
482         i = reverse_find_non_whitespace( get_text( &piter->member.run, 0 ),
483                                          piter->member.run.len );
484         pp = split_run_extents(wc, piter, i);
485         wc->pt = pp->member.run.pt;
486         return pp;
487       }
488       /* this run is the end of spaces, so the run edge is a good point to split */
489       wc->pt = pp->member.run.pt;
490       wc->bOverflown = TRUE;
491       TRACE("Split point is: %s|%s\n", debugstr_run( &piter->member.run ), debugstr_run( &pp->member.run ));
492       return pp;
493     }
494     wc->pt = piter->member.run.pt;
495     return piter;
496   }
497 }
498 
499 static ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc)
500 {
501   ME_DisplayItem *piter = p, *pp;
502   int i, idesp, len;
503   ME_Run *run = &p->member.run;
504 
505   idesp = i = find_split_point( wc->context, loc, run );
506   len = run->len;
507   assert(len>0);
508   assert(i<len);
509   if (i) {
510     /* don't split words */
511     i = reverse_find_whitespace( get_text( run, 0 ), i );
512     pp = ME_MaximizeSplit(wc, p, i);
513     if (pp)
514       return pp;
515   }
516   TRACE("Must backtrack to split at: %s\n", debugstr_run( &p->member.run ));
517   if (wc->pLastSplittableRun)
518   {
519     if (wc->pLastSplittableRun->member.run.nFlags & (MERF_GRAPHICS|MERF_TAB))
520     {
521       wc->pt = wc->pLastSplittableRun->member.run.pt;
522       return wc->pLastSplittableRun;
523     }
524     else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)
525     {
526       /* the following two lines are just to check if we forgot to call UpdateRunFlags earlier,
527          they serve no other purpose */
528       ME_UpdateRunFlags(wc->context->editor, run);
529       assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE));
530 
531       piter = wc->pLastSplittableRun;
532       run = &piter->member.run;
533       len = run->len;
534       /* don't split words */
535       i = reverse_find_whitespace( get_text( run, 0 ), len );
536       if (i == len)
537         i = reverse_find_non_whitespace( get_text( run, 0 ), len );
538       if (i) {
539         ME_DisplayItem *piter2 = split_run_extents(wc, piter, i);
540         wc->pt = piter2->member.run.pt;
541         return piter2;
542       }
543       /* splittable = must have whitespaces */
544       assert(0 == "Splittable, but no whitespaces");
545     }
546     else
547     {
548       /* restart from the first run beginning with spaces */
549       wc->pt = wc->pLastSplittableRun->member.run.pt;
550       return wc->pLastSplittableRun;
551     }
552   }
553   TRACE("Backtracking failed, trying desperate: %s\n", debugstr_run( &p->member.run ));
554   /* OK, no better idea, so assume we MAY split words if we can split at all*/
555   if (idesp)
556     return split_run_extents(wc, piter, idesp);
557   else
558   if (wc->pRowStart && piter != wc->pRowStart)
559   {
560     /* don't need to break current run, because it's possible to split
561        before this run */
562     wc->bOverflown = TRUE;
563     return piter;
564   }
565   else
566   {
567     /* split point inside first character - no choice but split after that char */
568     if (len != 1) {
569       /* the run is more than 1 char, so we may split */
570       return split_run_extents(wc, piter, 1);
571     }
572     /* the run is one char, can't split it */
573     return piter;
574   }
575 }
576 
577 static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
578 {
579   ME_DisplayItem *pp;
580   ME_Run *run;
581   int len;
582 
583   assert(p->type == diRun);
584   if (!wc->pRowStart)
585     wc->pRowStart = p;
586   run = &p->member.run;
587   run->pt.x = wc->pt.x;
588   run->pt.y = wc->pt.y;
589   ME_WrapSizeRun(wc, p);
590   len = run->len;
591 
592   if (wc->bOverflown) /* just skipping final whitespaces */
593   {
594     /* End paragraph run can't overflow to the next line by itself. */
595     if (run->nFlags & MERF_ENDPARA)
596       return p->next;
597 
598     if (run->nFlags & MERF_WHITESPACE) {
599       wc->pt.x += run->nWidth;
600       /* skip runs consisting of only whitespaces */
601       return p->next;
602     }
603 
604     if (run->nFlags & MERF_STARTWHITE) {
605       /* try to split the run at the first non-white char */
606       int black;
607       black = find_non_whitespace( get_text( run, 0 ), run->len, 0 );
608       if (black) {
609         wc->bOverflown = FALSE;
610         pp = split_run_extents(wc, p, black);
611         calc_run_extent(wc->context, &wc->pPara->member.para,
612                         wc->nRow ? wc->nLeftMargin : wc->nFirstMargin,
613                         &pp->member.run);
614         ME_InsertRowStart(wc, pp);
615         return pp;
616       }
617     }
618     /* black run: the row goes from pRowStart to the previous run */
619     ME_InsertRowStart(wc, p);
620     return p;
621   }
622   /* simply end the current row and move on to next one */
623   if (run->nFlags & MERF_ENDROW)
624   {
625     p = p->next;
626     ME_InsertRowStart(wc, p);
627     return p;
628   }
629 
630   /* will current run fit? */
631   if (wc->bWordWrap &&
632       wc->pt.x + run->nWidth - wc->context->pt.x > wc->nAvailWidth)
633   {
634     int loc = wc->context->pt.x + wc->nAvailWidth - wc->pt.x;
635     /* total white run or end para */
636     if (run->nFlags & (MERF_WHITESPACE | MERF_ENDPARA)) {
637       /* let the overflow logic handle it */
638       wc->bOverflown = TRUE;
639       return p;
640     }
641     /* TAB: we can split before */
642     if (run->nFlags & MERF_TAB) {
643       wc->bOverflown = TRUE;
644       if (wc->pRowStart == p)
645         /* Don't split before the start of the run, or we will get an
646          * endless loop. */
647         return p->next;
648       else
649         return p;
650     }
651     /* graphics: we can split before, if run's width is smaller than row's width */
652     if ((run->nFlags & MERF_GRAPHICS) && run->nWidth <= wc->nAvailWidth) {
653       wc->bOverflown = TRUE;
654       return p;
655     }
656     /* can we separate out the last spaces ? (to use overflow logic later) */
657     if (run->nFlags & MERF_ENDWHITE)
658     {
659       /* we aren't sure if it's *really* necessary, it's a good start however */
660       int black = reverse_find_non_whitespace( get_text( run, 0 ), len );
661       split_run_extents(wc, p, black);
662       /* handle both parts again */
663       return p;
664     }
665     /* determine the split point by backtracking */
666     pp = ME_SplitByBacktracking(wc, p, loc);
667     if (pp == wc->pRowStart)
668     {
669       if (run->nFlags & MERF_STARTWHITE)
670       {
671           /* We had only spaces so far, so we must be on the first line of the
672            * paragraph (or the first line after MERF_ENDROW forced the line
673            * break within the paragraph), since no other lines of the paragraph
674            * start with spaces. */
675 
676           /* The lines will only contain spaces, and the rest of the run will
677            * overflow onto the next line. */
678           wc->bOverflown = TRUE;
679           return p;
680       }
681       /* Couldn't split the first run, possible because we have a large font
682        * with a single character that caused an overflow.
683        */
684       wc->pt.x += run->nWidth;
685       return p->next;
686     }
687     if (p != pp) /* found a suitable split point */
688     {
689       wc->bOverflown = TRUE;
690       return pp;
691     }
692     /* we detected that it's best to split on start of this run */
693     if (wc->bOverflown)
694       return pp;
695     ERR("failure!\n");
696     /* not found anything - writing over margins is the only option left */
697   }
698   if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
699     || ((run->nFlags & (MERF_GRAPHICS|MERF_TAB)) && (p != wc->pRowStart)))
700   {
701     wc->pLastSplittableRun = p;
702   }
703   wc->pt.x += run->nWidth;
704   return p->next;
705 }
706 
707 static int ME_GetParaLineSpace(ME_Context* c, ME_Paragraph* para)
708 {
709   int   sp = 0, ls = 0;
710   if (!(para->fmt.dwMask & PFM_LINESPACING)) return 0;
711 
712   /* FIXME: how to compute simply the line space in ls ??? */
713   /* FIXME: does line spacing include the line itself ??? */
714   switch (para->fmt.bLineSpacingRule)
715   {
716   case 0:       sp = ls; break;
717   case 1:       sp = (3 * ls) / 2; break;
718   case 2:       sp = 2 * ls; break;
719   case 3:       sp = ME_twips2pointsY(c, para->fmt.dyLineSpacing); if (sp < ls) sp = ls; break;
720   case 4:       sp = ME_twips2pointsY(c, para->fmt.dyLineSpacing); break;
721   case 5:       sp = para->fmt.dyLineSpacing / 20; break;
722   default: FIXME("Unsupported spacing rule value %d\n", para->fmt.bLineSpacingRule);
723   }
724   if (c->editor->nZoomNumerator == 0)
725     return sp;
726   else
727     return sp * c->editor->nZoomNumerator / c->editor->nZoomDenominator;
728 }
729 
730 static void ME_PrepareParagraphForWrapping(ME_TextEditor *editor, ME_Context *c, ME_DisplayItem *tp) {
731   ME_DisplayItem *p;
732 
733   tp->member.para.nWidth = 0;
734   /* remove row start items as they will be reinserted by the
735    * paragraph wrapper anyway */
736   editor->total_rows -= tp->member.para.nRows;
737   tp->member.para.nRows = 0;
738   for (p = tp->next; p != tp->member.para.next_para; p = p->next) {
739     if (p->type == diStartRow) {
740       ME_DisplayItem *pRow = p;
741       p = p->prev;
742       ME_Remove(pRow);
743       ME_DestroyDisplayItem(pRow);
744     }
745   }
746   /* join runs that can be joined */
747   for (p = tp->next; p != tp->member.para.next_para; p = p->next) {
748     assert(p->type != diStartRow); /* should have been deleted above */
749     if (p->type == diRun) {
750       while (p->next->type == diRun && /* FIXME */
751              ME_CanJoinRuns(&p->member.run, &p->next->member.run)) {
752         ME_JoinRuns(c->editor, p);
753       }
754     }
755   }
756 }
757 
758 static HRESULT itemize_para( ME_Context *c, ME_DisplayItem *p )
759 {
760     ME_Paragraph *para = &p->member.para;
761     ME_Run *run;
762     ME_DisplayItem *di;
763     SCRIPT_ITEM buf[16], *items = buf;
764     int items_passed = ARRAY_SIZE( buf ), num_items, cur_item;
765     SCRIPT_CONTROL control = { LANG_USER_DEFAULT, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
766                                FALSE, FALSE, 0 };
767     SCRIPT_STATE state = { 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, 0 };
768     HRESULT hr;
769 
770     assert( p->type == diParagraph );
771 
772     if (para->fmt.dwMask & PFM_RTLPARA && para->fmt.wEffects & PFE_RTLPARA)
773         state.uBidiLevel = 1;
774 
775     TRACE( "Base embedding level %d\n", state.uBidiLevel );
776 
777     while (1)
778     {
779         hr = ScriptItemize( para->text->szData, para->text->nLen, items_passed, &control,
780                             &state, items, &num_items );
781         if (hr != E_OUTOFMEMORY) break; /* may not be enough items if hr == E_OUTOFMEMORY */
782         if (items_passed > para->text->nLen + 1) break; /* something else has gone wrong */
783         items_passed *= 2;
784         if (items == buf)
785             items = heap_alloc( items_passed * sizeof( *items ) );
786         else
787             items = heap_realloc( items, items_passed * sizeof( *items ) );
788         if (!items) break;
789     }
790     if (FAILED( hr )) goto end;
791 
792     if (TRACE_ON( richedit ))
793     {
794         TRACE( "got items:\n" );
795         for (cur_item = 0; cur_item < num_items; cur_item++)
796         {
797             TRACE( "\t%d - %d RTL %d bidi level %d\n", items[cur_item].iCharPos, items[cur_item+1].iCharPos - 1,
798                    items[cur_item].a.fRTL, items[cur_item].a.s.uBidiLevel );
799         }
800 
801         TRACE( "before splitting runs into ranges\n" );
802         for (di = p->next; di != p->member.para.next_para; di = di->next)
803         {
804             if (di->type != diRun) continue;
805             TRACE( "\t%d: %s\n", di->member.run.nCharOfs, debugstr_run( &di->member.run ) );
806         }
807     }
808 
809     /* split runs into ranges at item boundaries */
810     for (di = p->next, cur_item = 0; di != p->member.para.next_para; di = di->next)
811     {
812         if (di->type != diRun) continue;
813         run = &di->member.run;
814 
815         if (run->nCharOfs == items[cur_item+1].iCharPos) cur_item++;
816 
817         items[cur_item].a.fLogicalOrder = TRUE;
818         run->script_analysis = items[cur_item].a;
819 
820         if (run->nFlags & MERF_ENDPARA) break; /* don't split eop runs */
821 
822         if (run->nCharOfs + run->len > items[cur_item+1].iCharPos)
823         {
824             ME_Cursor cursor = {p, di, items[cur_item+1].iCharPos - run->nCharOfs};
825             ME_SplitRunSimple( c->editor, &cursor );
826         }
827     }
828 
829     if (TRACE_ON( richedit ))
830     {
831         TRACE( "after splitting into ranges\n" );
832         for (di = p->next; di != p->member.para.next_para; di = di->next)
833         {
834             if (di->type != diRun) continue;
835             TRACE( "\t%d: %s\n", di->member.run.nCharOfs, debugstr_run( &di->member.run ) );
836         }
837     }
838 
839     para->nFlags |= MEPF_COMPLEX;
840 
841 end:
842     if (items != buf) heap_free( items );
843     return hr;
844 }
845 
846 
847 static HRESULT shape_para( ME_Context *c, ME_DisplayItem *p )
848 {
849     ME_DisplayItem *di;
850     ME_Run *run;
851     HRESULT hr;
852 
853     for (di = p->next; di != p->member.para.next_para; di = di->next)
854     {
855         if (di->type != diRun) continue;
856         run = &di->member.run;
857 
858         hr = shape_run( c, run );
859         if (FAILED( hr ))
860         {
861             run->para->nFlags &= ~MEPF_COMPLEX;
862             return hr;
863         }
864     }
865     return hr;
866 }
867 
868 static void ME_WrapTextParagraph(ME_TextEditor *editor, ME_Context *c, ME_DisplayItem *tp) {
869   ME_DisplayItem *p;
870   ME_WrapContext wc;
871   int border = 0;
872   int linespace = 0;
873   PARAFORMAT2 *pFmt;
874 
875   assert(tp->type == diParagraph);
876   if (!(tp->member.para.nFlags & MEPF_REWRAP)) {
877     return;
878   }
879   ME_PrepareParagraphForWrapping(editor, c, tp);
880 
881   /* Calculate paragraph numbering label */
882   para_num_init( c, &tp->member.para );
883 
884   /* For now treating all non-password text as complex for better testing */
885   if (!c->editor->cPasswordMask /* &&
886       ScriptIsComplex( tp->member.para.text->szData, tp->member.para.text->nLen, SIC_COMPLEX ) == S_OK */)
887   {
888       if (SUCCEEDED( itemize_para( c, tp ) ))
889           shape_para( c, tp );
890   }
891 
892   pFmt = &tp->member.para.fmt;
893 
894   wc.context = c;
895   wc.pPara = tp;
896 /*   wc.para_style = tp->member.para.style; */
897   wc.style = NULL;
898   wc.nParaNumOffset = 0;
899   if (tp->member.para.nFlags & MEPF_ROWEND) {
900     wc.nFirstMargin = wc.nLeftMargin = wc.nRightMargin = 0;
901   } else {
902     int dxStartIndent = pFmt->dxStartIndent;
903     if (tp->member.para.pCell) {
904       dxStartIndent += ME_GetTableRowEnd(tp)->member.para.fmt.dxOffset;
905     }
906     wc.nLeftMargin = ME_twips2pointsX(c, dxStartIndent + pFmt->dxOffset);
907     wc.nFirstMargin = ME_twips2pointsX(c, dxStartIndent);
908     if (pFmt->wNumbering)
909     {
910         wc.nParaNumOffset = wc.nFirstMargin;
911         dxStartIndent = max( ME_twips2pointsX(c, pFmt->wNumberingTab),
912                              tp->member.para.para_num.width );
913         wc.nFirstMargin += dxStartIndent;
914     }
915     wc.nRightMargin = ME_twips2pointsX(c, pFmt->dxRightIndent);
916 
917     if (wc.nFirstMargin < 0)
918         wc.nFirstMargin = 0;
919     if (wc.nLeftMargin < 0)
920         wc.nLeftMargin = 0;
921   }
922   if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
923       pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
924   {
925     wc.nFirstMargin += ME_twips2pointsX(c, pFmt->dxOffset * 2);
926   }
927   wc.nRow = 0;
928   wc.pt.y = 0;
929   if (pFmt->dwMask & PFM_SPACEBEFORE)
930     wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceBefore);
931   if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) &&
932       pFmt->dwMask & PFM_BORDER)
933   {
934     border = ME_GetParaBorderWidth(c, tp->member.para.fmt.wBorders);
935     if (pFmt->wBorders & 1) {
936       wc.nFirstMargin += border;
937       wc.nLeftMargin += border;
938     }
939     if (pFmt->wBorders & 2)
940       wc.nRightMargin -= border;
941     if (pFmt->wBorders & 4)
942       wc.pt.y += border;
943   }
944 
945   linespace = ME_GetParaLineSpace(c, &tp->member.para);
946 
947   ME_BeginRow(&wc);
948   for (p = tp->next; p!=tp->member.para.next_para; ) {
949     assert(p->type != diStartRow);
950     if (p->type == diRun) {
951       p = ME_WrapHandleRun(&wc, p);
952     }
953     else p = p->next;
954     if (wc.nRow && p == wc.pRowStart)
955       wc.pt.y += linespace;
956   }
957   ME_WrapEndParagraph(&wc, p);
958   if (!(pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) &&
959       (pFmt->dwMask & PFM_BORDER) && (pFmt->wBorders & 8))
960     wc.pt.y += border;
961   if (tp->member.para.fmt.dwMask & PFM_SPACEAFTER)
962     wc.pt.y += ME_twips2pointsY(c, pFmt->dySpaceAfter);
963 
964   tp->member.para.nFlags &= ~MEPF_REWRAP;
965   tp->member.para.nHeight = wc.pt.y;
966   tp->member.para.nRows = wc.nRow;
967   editor->total_rows += wc.nRow;
968 }
969 
970 static void ME_MarkRepaintEnd(ME_DisplayItem *para,
971                               ME_DisplayItem **repaint_start,
972                               ME_DisplayItem **repaint_end)
973 {
974     if (!*repaint_start)
975       *repaint_start = para;
976     *repaint_end = para;
977 }
978 
979 static void adjust_para_y(ME_DisplayItem *item, ME_Context *c, ME_DisplayItem *repaint_start, ME_DisplayItem *repaint_end)
980 {
981     if (item->member.para.nFlags & MEPF_ROWSTART)
982     {
983         ME_DisplayItem *cell = ME_FindItemFwd(item, diCell);
984         ME_DisplayItem *endRowPara;
985         int borderWidth = 0;
986         cell->member.cell.pt = c->pt;
987         /* Offset the text by the largest top border width. */
988         while (cell->member.cell.next_cell)
989         {
990             borderWidth = max(borderWidth, cell->member.cell.border.top.width);
991             cell = cell->member.cell.next_cell;
992         }
993         endRowPara = ME_FindItemFwd(cell, diParagraph);
994         assert(endRowPara->member.para.nFlags & MEPF_ROWEND);
995         if (borderWidth > 0)
996         {
997             borderWidth = max(ME_twips2pointsY(c, borderWidth), 1);
998             while (cell)
999             {
1000                 cell->member.cell.yTextOffset = borderWidth;
1001                 cell = cell->member.cell.prev_cell;
1002             }
1003             c->pt.y += borderWidth;
1004         }
1005         if (endRowPara->member.para.fmt.dxStartIndent > 0)
1006         {
1007             int dxStartIndent = endRowPara->member.para.fmt.dxStartIndent;
1008             cell = ME_FindItemFwd(item, diCell);
1009             cell->member.cell.pt.x += ME_twips2pointsX(c, dxStartIndent);
1010             c->pt.x = cell->member.cell.pt.x;
1011         }
1012     }
1013     else if (item->member.para.nFlags & MEPF_ROWEND)
1014     {
1015         /* Set all the cells to the height of the largest cell */
1016         ME_DisplayItem *startRowPara;
1017         int prevHeight, nHeight, bottomBorder = 0;
1018         ME_DisplayItem *cell = ME_FindItemBack(item, diCell);
1019         item->member.para.nWidth = cell->member.cell.pt.x + cell->member.cell.nWidth;
1020         if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWSTART))
1021         {
1022             /* Last row, the bottom border is added to the height. */
1023             cell = cell->member.cell.prev_cell;
1024             while (cell)
1025             {
1026                 bottomBorder = max(bottomBorder, cell->member.cell.border.bottom.width);
1027                 cell = cell->member.cell.prev_cell;
1028             }
1029             bottomBorder = ME_twips2pointsY(c, bottomBorder);
1030             cell = ME_FindItemBack(item, diCell);
1031         }
1032         prevHeight = cell->member.cell.nHeight;
1033         nHeight = cell->member.cell.prev_cell->member.cell.nHeight + bottomBorder;
1034         cell->member.cell.nHeight = nHeight;
1035         item->member.para.nHeight = nHeight;
1036         cell = cell->member.cell.prev_cell;
1037         cell->member.cell.nHeight = nHeight;
1038         while (cell->member.cell.prev_cell)
1039         {
1040             cell = cell->member.cell.prev_cell;
1041             cell->member.cell.nHeight = nHeight;
1042         }
1043         /* Also set the height of the start row paragraph */
1044         startRowPara = ME_FindItemBack(cell, diParagraph);
1045         startRowPara->member.para.nHeight = nHeight;
1046         c->pt.x = startRowPara->member.para.pt.x;
1047         c->pt.y = cell->member.cell.pt.y + nHeight;
1048         if (prevHeight < nHeight)
1049         {
1050             /* The height of the cells has grown, so invalidate the bottom of
1051              * the cells. */
1052             ME_MarkRepaintEnd(item, &repaint_start, &repaint_end);
1053             cell = ME_FindItemBack(item, diCell);
1054             while (cell)
1055             {
1056                 ME_MarkRepaintEnd(ME_FindItemBack(cell, diParagraph), &repaint_start, &repaint_end);
1057                 cell = cell->member.cell.prev_cell;
1058             }
1059         }
1060     }
1061     else if (item->member.para.pCell &&
1062              item->member.para.pCell != item->member.para.next_para->member.para.pCell)
1063     {
1064         /* The next paragraph is in the next cell in the table row. */
1065         ME_Cell *cell = &item->member.para.pCell->member.cell;
1066         cell->nHeight = c->pt.y + item->member.para.nHeight - cell->pt.y;
1067 
1068         /* Propagate the largest height to the end so that it can be easily
1069          * sent back to all the cells at the end of the row. */
1070         if (cell->prev_cell)
1071             cell->nHeight = max(cell->nHeight, cell->prev_cell->member.cell.nHeight);
1072 
1073         c->pt.x = cell->pt.x + cell->nWidth;
1074         c->pt.y = cell->pt.y;
1075         cell->next_cell->member.cell.pt = c->pt;
1076         if (!(item->member.para.next_para->member.para.nFlags & MEPF_ROWEND))
1077             c->pt.y += cell->yTextOffset;
1078     }
1079     else
1080     {
1081         if (item->member.para.pCell)
1082         {
1083             /* Next paragraph in the same cell. */
1084             c->pt.x = item->member.para.pCell->member.cell.pt.x;
1085         }
1086         else
1087             /* Normal paragraph */
1088             c->pt.x = 0;
1089         c->pt.y += item->member.para.nHeight;
1090     }
1091 }
1092 
1093 BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor)
1094 {
1095   ME_DisplayItem *item;
1096   ME_Context c;
1097   int totalWidth = editor->nTotalWidth, diff = 0, prev_width;
1098   ME_DisplayItem *repaint_start = NULL, *repaint_end = NULL;
1099   ME_Paragraph *para;
1100 
1101   if (!editor->first_marked_para)
1102     return FALSE;
1103 
1104   ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
1105 
1106   item = editor->first_marked_para;
1107   c.pt = item->member.para.pt;
1108   while (item != editor->pBuffer->pLast)
1109   {
1110     assert(item->type == diParagraph);
1111 
1112     prev_width = item->member.para.nWidth;
1113     ME_WrapTextParagraph(editor, &c, item);
1114     if (prev_width == totalWidth && item->member.para.nWidth < totalWidth)
1115       totalWidth = get_total_width(editor);
1116     else
1117       totalWidth = max(totalWidth, item->member.para.nWidth);
1118 
1119     if (!item->member.para.nCharOfs)
1120       ME_MarkRepaintEnd(item->member.para.prev_para, &repaint_start, &repaint_end);
1121     ME_MarkRepaintEnd(item, &repaint_start, &repaint_end);
1122     adjust_para_y(item, &c, repaint_start, repaint_end);
1123 
1124     if (item->member.para.next_para)
1125     {
1126       diff = c.pt.y - item->member.para.next_para->member.para.pt.y;
1127       if (diff)
1128       {
1129         para = &item->member.para;
1130         while (para->next_para && para != &item->member.para.next_marked->member.para &&
1131                para != &editor->pBuffer->pLast->member.para)
1132         {
1133           ME_MarkRepaintEnd(para->next_para, &repaint_start, &repaint_end);
1134           para->next_para->member.para.pt.y = c.pt.y;
1135           adjust_para_y(para->next_para, &c, repaint_start, repaint_end);
1136           para = &para->next_para->member.para;
1137         }
1138       }
1139     }
1140     if (item->member.para.next_marked)
1141     {
1142       ME_DisplayItem *rem = item;
1143       item = item->member.para.next_marked;
1144       remove_marked_para(editor, rem);
1145     }
1146     else
1147     {
1148       remove_marked_para(editor, item);
1149       item = editor->pBuffer->pLast;
1150     }
1151     c.pt.y = item->member.para.pt.y;
1152   }
1153   editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
1154   editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
1155 
1156   editor->nTotalLength = c.pt.y;
1157   editor->nTotalWidth = totalWidth;
1158   editor->pBuffer->pLast->member.para.pt.x = 0;
1159   editor->pBuffer->pLast->member.para.pt.y = c.pt.y;
1160 
1161   ME_DestroyContext(&c);
1162 
1163   if (repaint_start || editor->nTotalLength < editor->nLastTotalLength)
1164     ME_InvalidateParagraphRange(editor, repaint_start, repaint_end);
1165   return !!repaint_start;
1166 }
1167 
1168 void ME_InvalidateParagraphRange(ME_TextEditor *editor,
1169                                  ME_DisplayItem *start_para,
1170                                  ME_DisplayItem *last_para)
1171 {
1172   ME_Context c;
1173   RECT rc;
1174   int ofs;
1175 
1176   ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
1177   rc = c.rcView;
1178   ofs = editor->vert_si.nPos;
1179 
1180   if (start_para) {
1181     start_para = ME_GetOuterParagraph(start_para);
1182     last_para = ME_GetOuterParagraph(last_para);
1183     rc.top = c.rcView.top + start_para->member.para.pt.y - ofs;
1184   } else {
1185     rc.top = c.rcView.top + editor->nTotalLength - ofs;
1186   }
1187   if (editor->nTotalLength < editor->nLastTotalLength)
1188     rc.bottom = c.rcView.top + editor->nLastTotalLength - ofs;
1189   else
1190     rc.bottom = c.rcView.top + last_para->member.para.pt.y + last_para->member.para.nHeight - ofs;
1191   ITextHost_TxInvalidateRect(editor->texthost, &rc, TRUE);
1192 
1193   ME_DestroyContext(&c);
1194 }
1195 
1196 
1197 void
1198 ME_SendRequestResize(ME_TextEditor *editor, BOOL force)
1199 {
1200   if (editor->nEventMask & ENM_REQUESTRESIZE)
1201   {
1202     RECT rc;
1203 
1204     ITextHost_TxGetClientRect(editor->texthost, &rc);
1205 
1206     if (force || rc.bottom != editor->nTotalLength)
1207     {
1208       REQRESIZE info;
1209 
1210       info.nmhdr.hwndFrom = NULL;
1211       info.nmhdr.idFrom = 0;
1212       info.nmhdr.code = EN_REQUESTRESIZE;
1213       info.rc = rc;
1214       info.rc.right = editor->nTotalWidth;
1215       info.rc.bottom = editor->nTotalLength;
1216 
1217       editor->nEventMask &= ~ENM_REQUESTRESIZE;
1218       ITextHost_TxNotify(editor->texthost, info.nmhdr.code, &info);
1219       editor->nEventMask |= ENM_REQUESTRESIZE;
1220     }
1221   }
1222 }
1223