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