ReactOS  0.4.14-dev-606-g14ebc0b
run.c
Go to the documentation of this file.
1 /*
2  * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs).
3  * Splitting/joining runs. Adjusting offsets after deleting/adding content.
4  * Character/pixel conversions.
5  *
6  * Copyright 2004 by Krzysztof Foltman
7  * Copyright 2006 by Phil Krylov
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "editor.h"
25 
27 WINE_DECLARE_DEBUG_CHANNEL(richedit_check);
28 WINE_DECLARE_DEBUG_CHANNEL(richedit_lists);
29 
30 /******************************************************************************
31  * ME_CanJoinRuns
32  *
33  * Returns TRUE if two runs can be safely merged into one, FALSE otherwise.
34  */
35 BOOL ME_CanJoinRuns(const ME_Run *run1, const ME_Run *run2)
36 {
37  if ((run1->nFlags | run2->nFlags) & MERF_NOJOIN)
38  return FALSE;
39  if (run1->style != run2->style)
40  return FALSE;
41  if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS))
42  return FALSE;
43  return TRUE;
44 }
45 
47 {
49  assert(p);
51 }
52 
53 /******************************************************************************
54  * ME_PropagateCharOffsets
55  *
56  * Shifts (increases or decreases) character offset (relative to beginning of
57  * the document) of the part of the text starting from given place.
58  */
60 {
61  /* Runs in one paragraph contain character offset relative to their owning
62  * paragraph. If we start the shifting from the run, we need to shift
63  * all the relative offsets until the end of the paragraph
64  */
65  if (p->type == diRun) /* propagate in all runs in this para */
66  {
67  TRACE("PropagateCharOffset(%s, %d)\n", debugstr_run( &p->member.run ), shift);
68  do {
69  p->member.run.nCharOfs += shift;
70  assert(p->member.run.nCharOfs >= 0);
72  } while(p->type == diRun);
73  }
74  /* Runs in next paragraphs don't need their offsets updated, because they,
75  * again, those offsets are relative to their respective paragraphs.
76  * Instead of that, we're updating paragraphs' character offsets.
77  */
78  if (p->type == diParagraph) /* propagate in all next paras */
79  {
80  do {
81  p->member.para.nCharOfs += shift;
82  assert(p->member.para.nCharOfs >= 0);
83  p = p->member.para.next_para;
84  } while(p->type == diParagraph);
85  }
86  /* diTextEnd also has character offset in it, which makes finding text length
87  * easier. But it needs to be up to date first.
88  */
89  if (p->type == diTextEnd)
90  {
91  p->member.para.nCharOfs += shift;
92  assert(p->member.para.nCharOfs >= 0);
93  }
94 }
95 
96 /******************************************************************************
97  * ME_CheckCharOffsets
98  *
99  * Checks if editor lists' validity and optionally dumps the document structure
100  */
102 {
103  ME_DisplayItem *p = editor->pBuffer->pFirst;
104  int ofs = 0, ofsp = 0;
105 
106  if (!TRACE_ON(richedit_check))
107  return;
108 
109  TRACE_(richedit_check)("Checking begin\n");
110  if(TRACE_ON(richedit_lists))
111  {
112  TRACE_(richedit_lists)("---\n");
113  ME_DumpDocument(editor->pBuffer);
114  }
115  do {
117  switch(p->type) {
118  case diTextEnd:
119  TRACE_(richedit_check)("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
120  assert(ofsp+ofs == p->member.para.nCharOfs);
121  TRACE_(richedit_check)("Checking finished\n");
122  return;
123  case diParagraph:
124  TRACE_(richedit_check)("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs);
125  assert(ofsp+ofs == p->member.para.nCharOfs);
126  ofsp = p->member.para.nCharOfs;
127  ofs = 0;
128  break;
129  case diRun:
130  TRACE_(richedit_check)("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = %s, flags=%08x, fx&mask = %08x\n",
131  p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
132  p->member.run.len, debugstr_run( &p->member.run ),
133  p->member.run.nFlags,
134  p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
135  assert(ofs == p->member.run.nCharOfs);
136  assert(p->member.run.len);
137  ofs += p->member.run.len;
138  break;
139  case diCell:
140  TRACE_(richedit_check)("cell\n");
141  break;
142  default:
143  assert(0);
144  }
145  } while(1);
146  TRACE_(richedit_check)("Checking finished\n");
147 }
148 
149 /******************************************************************************
150  * ME_CharOfsFromRunOfs
151  *
152  * Converts a character position relative to the start of the run, to a
153  * character position relative to the start of the document.
154  * Kind of a "local to global" offset conversion.
155  */
157  const ME_DisplayItem *pRun, int nOfs)
158 {
159  assert(pRun && pRun->type == diRun);
160  assert(pPara && pPara->type == diParagraph);
161  return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs + nOfs;
162 }
163 
164 /******************************************************************************
165  * ME_CursorFromCharOfs
166  *
167  * Converts a character offset (relative to the start of the document) to
168  * a cursor structure (which contains a run and a position relative to that
169  * run).
170  */
171 void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
172 {
173  ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pPara,
174  &pCursor->pRun, &pCursor->nOffset);
175 }
176 
177 /******************************************************************************
178  * ME_RunOfsFromCharOfs
179  *
180  * Find a run and relative character offset given an absolute character offset
181  * (absolute offset being an offset relative to the start of the document).
182  * Kind of a "global to local" offset conversion.
183  */
185  int nCharOfs,
186  ME_DisplayItem **ppPara,
187  ME_DisplayItem **ppRun,
188  int *pOfs)
189 {
191  int endOfs = nCharOfs, len = ME_GetTextLength(editor);
192 
193  nCharOfs = max(nCharOfs, 0);
194  nCharOfs = min(nCharOfs, len);
195 
196  /* Find the paragraph at the offset. */
198  do {
199  item = next_item;
200  next_item = item->member.para.next_para;
201  } while (next_item->member.para.nCharOfs <= nCharOfs);
202  assert(item->type == diParagraph);
203  nCharOfs -= item->member.para.nCharOfs;
204  if (ppPara) *ppPara = item;
205 
206  /* Find the run at the offset. */
208  do {
209  item = next_item;
211  } while (next_item->type == diRun &&
212  next_item->member.run.nCharOfs <= nCharOfs);
213  assert(item->type == diRun);
214  nCharOfs -= item->member.run.nCharOfs;
215 
216  if (ppRun) *ppRun = item;
217  if (pOfs) {
218  if (((*ppRun)->member.run.nFlags & MERF_ENDPARA) && endOfs > len)
219  *pOfs = (*ppRun)->member.run.len;
220  else *pOfs = nCharOfs;
221  }
222 }
223 
224 /******************************************************************************
225  * ME_JoinRuns
226  *
227  * Merges two adjacent runs, the one given as a parameter and the next one.
228  */
230 {
231  ME_DisplayItem *pNext = p->next;
232  int i;
233  assert(p->type == diRun && pNext->type == diRun);
234  assert(p->member.run.nCharOfs != -1);
236 
237  /* Update all cursors so that they don't contain the soon deleted run */
238  for (i=0; i<editor->nCursors; i++) {
239  if (editor->pCursors[i].pRun == pNext) {
240  editor->pCursors[i].pRun = p;
241  editor->pCursors[i].nOffset += p->member.run.len;
242  }
243  }
244 
245  p->member.run.len += pNext->member.run.len;
246  ME_Remove(pNext);
247  ME_DestroyDisplayItem(pNext);
248  ME_UpdateRunFlags(editor, &p->member.run);
249  ME_CheckCharOffsets(editor);
250 }
251 
252 /******************************************************************************
253  * ME_SplitRunSimple
254  *
255  * Does the most basic job of splitting a run into two - it does not
256  * update the positions and extents.
257  */
259 {
260  ME_DisplayItem *run = cursor->pRun;
261  ME_DisplayItem *new_run;
262  int i;
263  int nOffset = cursor->nOffset;
264 
265  assert(!(run->member.run.nFlags & MERF_NONTEXT));
266 
267  new_run = ME_MakeRun(run->member.run.style,
269  new_run->member.run.nCharOfs = run->member.run.nCharOfs + nOffset;
270  new_run->member.run.len = run->member.run.len - nOffset;
271  new_run->member.run.para = run->member.run.para;
272  run->member.run.len = nOffset;
273  cursor->pRun = new_run;
274  cursor->nOffset = 0;
275 
276  ME_InsertBefore(run->next, new_run);
277 
278  ME_UpdateRunFlags(editor, &run->member.run);
279  ME_UpdateRunFlags(editor, &new_run->member.run);
280  for (i = 0; i < editor->nCursors; i++) {
281  if (editor->pCursors[i].pRun == run &&
282  editor->pCursors[i].nOffset >= nOffset) {
283  editor->pCursors[i].pRun = new_run;
284  editor->pCursors[i].nOffset -= nOffset;
285  }
286  }
287  mark_para_rewrap(editor, cursor->pPara);
288  return run;
289 }
290 
291 /******************************************************************************
292  * ME_MakeRun
293  *
294  * A helper function to create run structures quickly.
295  */
297 {
299  item->member.run.style = s;
300  item->member.run.reobj = NULL;
301  item->member.run.nFlags = nFlags;
302  item->member.run.nCharOfs = -1;
303  item->member.run.len = 0;
304  item->member.run.para = NULL;
305  item->member.run.num_glyphs = 0;
306  item->member.run.max_glyphs = 0;
307  item->member.run.glyphs = NULL;
308  item->member.run.vis_attrs = NULL;
309  item->member.run.advances = NULL;
310  item->member.run.offsets = NULL;
311  item->member.run.max_clusters = 0;
312  item->member.run.clusters = NULL;
313  ME_AddRefStyle(s);
314  return item;
315 }
316 
317 /******************************************************************************
318  * ME_InsertRunAtCursor
319  *
320  * Inserts a new run with given style, flags and content at a given position,
321  * which is passed as a cursor structure (which consists of a run and
322  * a run-relative character offset).
323  */
326  const WCHAR *str, int len, int flags)
327 {
328  ME_DisplayItem *pDI, *insert_before = cursor->pRun, *prev;
329 
330  if (cursor->nOffset)
331  {
332  if (cursor->nOffset == cursor->pRun->member.run.len)
333  {
334  insert_before = ME_FindItemFwd( cursor->pRun, diRun );
335  if (!insert_before) insert_before = cursor->pRun; /* Always insert before the final eop run */
336  }
337  else
338  {
339  ME_SplitRunSimple( editor, cursor );
340  insert_before = cursor->pRun;
341  }
342  }
343 
344  add_undo_delete_run( editor, insert_before->member.run.para->nCharOfs +
345  insert_before->member.run.nCharOfs, len );
346 
347  pDI = ME_MakeRun(style, flags);
348  pDI->member.run.nCharOfs = insert_before->member.run.nCharOfs;
349  pDI->member.run.len = len;
350  pDI->member.run.para = insert_before->member.run.para;
351  ME_InsertString( pDI->member.run.para->text, pDI->member.run.nCharOfs, str, len );
352  ME_InsertBefore( insert_before, pDI );
353  TRACE("Shift length:%d\n", len);
354  ME_PropagateCharOffset( insert_before, len );
355  mark_para_rewrap(editor, get_di_from_para(insert_before->member.run.para));
356 
357  /* Move any cursors that were at the end of the previous run to the end of the inserted run */
358  prev = ME_FindItemBack( pDI, diRun );
359  if (prev)
360  {
361  int i;
362 
363  for (i = 0; i < editor->nCursors; i++)
364  {
365  if (editor->pCursors[i].pRun == prev &&
366  editor->pCursors[i].nOffset == prev->member.run.len)
367  {
368  editor->pCursors[i].pRun = pDI;
369  editor->pCursors[i].nOffset = len;
370  }
371  }
372  }
373 
374  return pDI;
375 }
376 
377 static BOOL run_is_splittable( const ME_Run *run )
378 {
379  WCHAR *str = get_text( run, 0 ), *p;
380  int i;
381  BOOL found_ink = FALSE;
382 
383  for (i = 0, p = str; i < run->len; i++, p++)
384  {
385  if (ME_IsWSpace( *p ))
386  {
387  if (found_ink) return TRUE;
388  }
389  else
390  found_ink = TRUE;
391  }
392  return FALSE;
393 }
394 
395 static BOOL run_is_entirely_ws( const ME_Run *run )
396 {
397  WCHAR *str = get_text( run, 0 ), *p;
398  int i;
399 
400  for (i = 0, p = str; i < run->len; i++, p++)
401  if (!ME_IsWSpace( *p )) return FALSE;
402 
403  return TRUE;
404 }
405 
406 /******************************************************************************
407  * ME_UpdateRunFlags
408  *
409  * Determine some of run attributes given its content (style, text content).
410  * Some flags cannot be determined by this function (MERF_GRAPHICS,
411  * MERF_ENDPARA)
412  */
414 {
415  assert(run->nCharOfs >= 0);
416 
417  if (RUN_IS_HIDDEN(run) || run->nFlags & MERF_TABLESTART)
418  run->nFlags |= MERF_HIDDEN;
419  else
420  run->nFlags &= ~MERF_HIDDEN;
421 
422  if (run_is_splittable( run ))
423  run->nFlags |= MERF_SPLITTABLE;
424  else
425  run->nFlags &= ~MERF_SPLITTABLE;
426 
427  if (!(run->nFlags & MERF_NOTEXT))
428  {
429  if (run_is_entirely_ws( run ))
431  else
432  {
433  run->nFlags &= ~MERF_WHITESPACE;
434 
435  if (ME_IsWSpace( *get_text( run, 0 ) ))
436  run->nFlags |= MERF_STARTWHITE;
437  else
438  run->nFlags &= ~MERF_STARTWHITE;
439 
440  if (ME_IsWSpace( *get_text( run, run->len - 1 ) ))
441  run->nFlags |= MERF_ENDWHITE;
442  else
443  run->nFlags &= ~MERF_ENDWHITE;
444  }
445  }
446  else
448 }
449 
450 /******************************************************************************
451  * ME_CharFromPointContext
452  *
453  * Returns a character position inside the run given a run-relative
454  * pixel horizontal position.
455  *
456  * If closest is FALSE return the actual character
457  * If closest is TRUE will round to the closest leading edge.
458  * ie. if the second character is at pixel position 8 and third at 16 then for:
459  * closest = FALSE cx = 0..7 return 0, cx = 8..15 return 1
460  * closest = TRUE cx = 0..3 return 0, cx = 4..11 return 1.
461  */
462 int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order)
463 {
464  ME_String *mask_text = NULL;
465  WCHAR *str;
466  int fit = 0;
467  SIZE sz, sz2, sz3;
468  if (!run->len || cx <= 0)
469  return 0;
470 
471  if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
472  {
473  if (!closest || cx < run->nWidth / 2) return 0;
474  return 1;
475  }
476 
477  if (run->nFlags & MERF_GRAPHICS)
478  {
479  SIZE sz;
480  ME_GetOLEObjectSize(c, run, &sz);
481  if (!closest || cx < sz.cx / 2) return 0;
482  return 1;
483  }
484 
485  if (run->para->nFlags & MEPF_COMPLEX)
486  {
487  int cp, trailing;
488  if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1;
489 
490  ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis,
491  &cp, &trailing );
492  TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth,
494  return closest ? cp + trailing : cp;
495  }
496 
497  if (c->editor->cPasswordMask)
498  {
499  mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len );
500  str = mask_text->szData;
501  }
502  else
503  str = get_text( run, 0 );
504 
505  select_style(c, run->style);
506  GetTextExtentExPointW(c->hDC, str, run->len,
507  cx, &fit, NULL, &sz);
508  if (closest && fit != run->len)
509  {
510  GetTextExtentPoint32W(c->hDC, str, fit, &sz2);
511  GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3);
512  if (cx >= (sz2.cx+sz3.cx)/2)
513  fit = fit + 1;
514  }
515 
516  ME_DestroyString( mask_text );
517 
518  return fit;
519 }
520 
521 int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run, BOOL closest, BOOL visual_order)
522 {
523  ME_Context c;
524  int ret;
525 
526  ME_InitContext( &c, editor, ITextHost_TxGetDC( editor->texthost ) );
527  ret = ME_CharFromPointContext( &c, cx, run, closest, visual_order );
529  return ret;
530 }
531 
532 /******************************************************************************
533  * ME_GetTextExtent
534  *
535  * Finds a width and a height of the text using a specified style
536  */
537 static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size)
538 {
539  if (c->hDC)
540  {
541  select_style( c, s );
542  GetTextExtentPoint32W( c->hDC, szText, nChars, size );
543  }
544  else
545  {
546  size->cx = 0;
547  size->cy = 0;
548  }
549 }
550 
551 /******************************************************************************
552  * ME_PointFromCharContext
553  *
554  * Returns a run-relative pixel position given a run-relative character
555  * position (character offset)
556  */
557 int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visual_order)
558 {
559  SIZE size;
560  ME_String *mask_text = NULL;
561  WCHAR *str;
562 
563  if (pRun->nFlags & MERF_GRAPHICS)
564  {
565  if (nOffset)
566  ME_GetOLEObjectSize(c, pRun, &size);
567  return nOffset != 0;
568  } else if (pRun->nFlags & MERF_ENDPARA) {
569  nOffset = 0;
570  }
571 
572  if (pRun->para->nFlags & MEPF_COMPLEX)
573  {
574  int x;
575  ScriptCPtoX( nOffset, FALSE, pRun->len, pRun->num_glyphs, pRun->clusters,
576  pRun->vis_attrs, pRun->advances, &pRun->script_analysis, &x );
577  if (visual_order && pRun->script_analysis.fRTL) x = pRun->nWidth - x - 1;
578  return x;
579  }
580  if (c->editor->cPasswordMask)
581  {
582  mask_text = ME_MakeStringR(c->editor->cPasswordMask, pRun->len);
583  str = mask_text->szData;
584  }
585  else
586  str = get_text( pRun, 0 );
587 
588  ME_GetTextExtent(c, str, nOffset, pRun->style, &size);
589  ME_DestroyString( mask_text );
590  return size.cx;
591 }
592 
593 /******************************************************************************
594  * ME_PointFromChar
595  *
596  * Calls ME_PointFromCharContext after first creating a context.
597  */
598 int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset, BOOL visual_order)
599 {
600  ME_Context c;
601  int ret;
602 
603  ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
604  ret = ME_PointFromCharContext( &c, pRun, nOffset, visual_order );
606 
607  return ret;
608 }
609 
610 /******************************************************************************
611  * ME_GetRunSizeCommon
612  *
613  * Finds width, height, ascent and descent of a run, up to given character
614  * (nLen).
615  */
616 SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen,
617  int startx, int *pAscent, int *pDescent)
618 {
619  static const WCHAR spaceW[] = {' ',0};
620  SIZE size;
621 
622  nLen = min( nLen, run->len );
623 
624  if (run->nFlags & MERF_ENDPARA)
625  {
626  nLen = min( nLen, 1 );
627  ME_GetTextExtent(c, spaceW, nLen, run->style, &size);
628  }
629  else if (para->nFlags & MEPF_COMPLEX)
630  {
631  size.cx = run->nWidth;
632  }
633  else if (c->editor->cPasswordMask)
634  {
635  ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen);
636  ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size);
637  ME_DestroyString(szMasked);
638  }
639  else
640  {
641  ME_GetTextExtent(c, get_text( run, 0 ), nLen, run->style, &size);
642  }
643  *pAscent = run->style->tm.tmAscent;
644  *pDescent = run->style->tm.tmDescent;
645  size.cy = *pAscent + *pDescent;
646 
647  if (run->nFlags & MERF_TAB)
648  {
649  int pos = 0, i = 0, ppos, shift = 0;
650  const PARAFORMAT2 *pFmt = &para->fmt;
651 
652  if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
653  pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
654  /* The horizontal gap shifts the tab positions to leave the gap. */
655  shift = pFmt->dxOffset * 2;
656  do {
657  if (i < pFmt->cTabCount)
658  {
659  /* Only one side of the horizontal gap is needed at the end of
660  * the table row. */
661  if (i == pFmt->cTabCount -1)
662  shift = shift >> 1;
663  pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF);
664  i++;
665  }
666  else
667  {
668  pos += lDefaultTab - (pos % lDefaultTab);
669  }
670  ppos = ME_twips2pointsX(c, pos);
671  if (ppos > startx + run->pt.x) {
672  size.cx = ppos - startx - run->pt.x;
673  break;
674  }
675  } while(1);
676  size.cy = *pAscent + *pDescent;
677  return size;
678  }
679  if (run->nFlags & MERF_GRAPHICS)
680  {
681  ME_GetOLEObjectSize(c, run, &size);
682  if (size.cy > *pAscent)
683  *pAscent = size.cy;
684  /* descent is unchanged */
685  return size;
686  }
687  return size;
688 }
689 
690 /******************************************************************************
691  * ME_SetSelectionCharFormat
692  *
693  * Applies a style change, either to a current selection, or to insert cursor
694  * (ie. the style next typed characters will use).
695  */
697 {
698  if (!ME_IsSelection(editor))
699  {
700  ME_Style *s;
701  if (!editor->pBuffer->pCharStyle)
702  editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
703  s = ME_ApplyStyle(editor, editor->pBuffer->pCharStyle, pFmt);
705  editor->pBuffer->pCharStyle = s;
706  } else {
707  ME_Cursor *from, *to;
708  ME_GetSelection(editor, &from, &to);
709  ME_SetCharFormat(editor, from, to, pFmt);
710  }
711 }
712 
713 /******************************************************************************
714  * ME_SetCharFormat
715  *
716  * Applies a style change to the specified part of the text
717  *
718  * The start and end cursors specify the part of the text. These cursors will
719  * be updated to stay valid, but this function may invalidate other
720  * non-selection cursors. The end cursor may be NULL to specify all the text
721  * following the start cursor.
722  *
723  * If no text is selected, then nothing is done.
724  */
726 {
727  ME_DisplayItem *run, *start_run = start->pRun, *end_run = NULL;
728 
729  if (end && start->pRun == end->pRun && start->nOffset == end->nOffset)
730  return;
731 
732  if (start->nOffset == start->pRun->member.run.len)
733  start_run = ME_FindItemFwd( start->pRun, diRun );
734  else if (start->nOffset)
735  {
736  /* SplitRunSimple may or may not update the cursors, depending on whether they
737  * are selection cursors, but we need to make sure they are valid. */
738  int split_offset = start->nOffset;
739  ME_DisplayItem *split_run = ME_SplitRunSimple(editor, start);
740  start_run = start->pRun;
741  if (end && end->pRun == split_run)
742  {
743  end->pRun = start->pRun;
744  end->nOffset -= split_offset;
745  }
746  }
747 
748  if (end)
749  {
750  if (end->nOffset == end->pRun->member.run.len)
751  end_run = ME_FindItemFwd( end->pRun, diRun );
752  else
753  {
754  if (end->nOffset) ME_SplitRunSimple(editor, end);
755  end_run = end->pRun;
756  }
757  }
758 
759  for (run = start_run; run != end_run; run = ME_FindItemFwd( run, diRun ))
760  {
761  ME_Style *new_style = ME_ApplyStyle(editor, run->member.run.style, pFmt);
762  ME_Paragraph *para = run->member.run.para;
763 
764  add_undo_set_char_fmt( editor, run->member.run.para->nCharOfs + run->member.run.nCharOfs,
765  run->member.run.len, &run->member.run.style->fmt );
767  run->member.run.style = new_style;
768 
769  /* The para numbering style depends on the eop style */
770  if ((run->member.run.nFlags & MERF_ENDPARA) && para->para_num.style)
771  {
772  ME_ReleaseStyle(para->para_num.style);
773  para->para_num.style = NULL;
774  }
775  mark_para_rewrap(editor, get_di_from_para(para));
776  }
777 }
778 
780 {
781  ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
782 }
783 
784 /******************************************************************************
785  * ME_GetDefaultCharFormat
786  *
787  * Retrieves the current default character style (the one applied where no
788  * other style was applied) .
789  */
791 {
792  ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
793 }
794 
795 /******************************************************************************
796  * ME_GetSelectionCharFormat
797  *
798  * If selection exists, it returns all style elements that are set consistently
799  * in the whole selection. If not, it just returns the current style.
800  */
802 {
803  ME_Cursor *from, *to;
804  if (!ME_IsSelection(editor) && editor->pBuffer->pCharStyle)
805  {
806  ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
807  return;
808  }
809  ME_GetSelection(editor, &from, &to);
810  ME_GetCharFormat(editor, from, to, pFmt);
811 }
812 
813 /******************************************************************************
814  * ME_GetCharFormat
815  *
816  * Returns the style consisting of those attributes which are consistently set
817  * in the whole character range.
818  */
820  const ME_Cursor *to, CHARFORMAT2W *pFmt)
821 {
822  ME_DisplayItem *run, *run_end;
823  CHARFORMAT2W tmp;
824 
825  run = from->pRun;
826  /* special case - if selection is empty, take previous char's formatting */
827  if (from->pRun == to->pRun && from->nOffset == to->nOffset)
828  {
829  if (!from->nOffset)
830  {
832  if (tmp_run->type == diRun) {
833  ME_GetRunCharFormat(editor, tmp_run, pFmt);
834  return;
835  }
836  }
837  ME_GetRunCharFormat(editor, run, pFmt);
838  return;
839  }
840 
841  run_end = to->pRun;
842  if (!to->nOffset)
843  run_end = ME_FindItemBack(run_end, diRun);
844 
845  ME_GetRunCharFormat(editor, run, pFmt);
846 
847  if (run == run_end) return;
848 
849  do {
850  /* FIXME add more style feature comparisons */
853 
854  run = ME_FindItemFwd(run, diRun);
855 
856  ZeroMemory(&tmp, sizeof(tmp));
857  tmp.cbSize = sizeof(tmp);
858  ME_GetRunCharFormat(editor, run, &tmp);
859 
860  assert((tmp.dwMask & dwAttribs) == dwAttribs);
861  /* reset flags that differ */
862 
863  if (pFmt->yHeight != tmp.yHeight)
864  pFmt->dwMask &= ~CFM_SIZE;
865  if (pFmt->dwMask & CFM_FACE)
866  {
867  if (!(tmp.dwMask & CFM_FACE))
868  pFmt->dwMask &= ~CFM_FACE;
869  else if (wcscmp(pFmt->szFaceName, tmp.szFaceName) ||
870  pFmt->bPitchAndFamily != tmp.bPitchAndFamily)
871  pFmt->dwMask &= ~CFM_FACE;
872  }
873  if (pFmt->yHeight != tmp.yHeight)
874  pFmt->dwMask &= ~CFM_SIZE;
875  if (pFmt->bUnderlineType != tmp.bUnderlineType)
876  pFmt->dwMask &= ~CFM_UNDERLINETYPE;
877  if (pFmt->dwMask & CFM_COLOR)
878  {
879  if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
880  {
881  if (pFmt->crTextColor != tmp.crTextColor)
882  pFmt->dwMask &= ~CFM_COLOR;
883  }
884  }
885 
886  pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & dwEffects);
887  pFmt->dwEffects = tmp.dwEffects;
888 
889  } while(run != run_end);
890 }
ME_DIType type
Definition: editstr.h:260
#define CFE_AUTOCOLOR
Definition: richedit.h:414
#define max(a, b)
Definition: svc.c:63
#define MERF_NONTEXT
Definition: editstr.h:115
ME_Paragraph para
Definition: editstr.h:266
#define TRUE
Definition: types.h:120
BOOL add_undo_set_char_fmt(ME_TextEditor *, int pos, int len, const CHARFORMAT2W *fmt) DECLSPEC_HIDDEN
Definition: undo.c:164
#define shift
Definition: input.c:1761
ME_Style * ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) DECLSPEC_HIDDEN
Definition: style.c:476
WCHAR * szData
Definition: editstr.h:60
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
BYTE bUnderlineType
Definition: richedit.h:321
#define MERF_NOTEXT
Definition: editstr.h:137
ME_Style * pCharStyle
Definition: editstr.h:273
long x
Definition: polytest.cpp:48
ME_DisplayItem * get_di_from_para(ME_Paragraph *para) DECLSPEC_HIDDEN
Definition: para.c:32
int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visual_order)
Definition: run.c:557
void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
Definition: run.c:696
#define MERF_STARTWHITE
Definition: editstr.h:120
static void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
Definition: run.c:779
#define CFM_FACE
Definition: richedit.h:360
#define CFM_PROTECTED
Definition: richedit.h:336
#define MERF_GRAPHICS
Definition: editstr.h:109
HRESULT WINAPI ScriptCPtoX(int iCP, BOOL fTrailing, int cChars, int cGlyphs, const WORD *pwLogClust, const SCRIPT_VISATTR *psva, const int *piAdvance, const SCRIPT_ANALYSIS *psa, int *piX)
Definition: usp10.c:2650
struct tagME_Paragraph * para
Definition: editstr.h:165
#define CFM_SUPERSCRIPT
Definition: richedit.h:349
#define assert(x)
Definition: debug.h:53
#define ZeroMemory
Definition: winbase.h:1642
GLint GLint GLint GLint GLint x
Definition: gl.h:1548
#define CFM_ITALIC
Definition: richedit.h:333
void ME_DumpDocument(ME_TextBuffer *buffer) DECLSPEC_HIDDEN
Definition: list.c:187
WORD * clusters
Definition: editstr.h:181
TEXTMETRICW tm
Definition: editstr.h:80
SHORT cTabCount
Definition: richedit.h:674
GLuint GLuint end
Definition: gl.h:1545
LONG tmDescent
Definition: wingdi.h:2384
LONG tmAscent
Definition: wingdi.h:2383
int ME_CharOfsFromRunOfs(ME_TextEditor *editor, const ME_DisplayItem *pPara, const ME_DisplayItem *pRun, int nOfs)
Definition: run.c:156
#define RUN_IS_HIDDEN(run)
Definition: editor.h:29
int nCharOfs
Definition: editstr.h:166
ME_Style * style
Definition: editstr.h:164
WINE_DECLARE_DEBUG_CHANNEL(richedit_check)
WINE_DEFAULT_DEBUG_CHANNEL(richedit)
void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift)
Definition: run.c:46
LONG rgxTabs[MAX_TAB_STOPS]
Definition: richedit.h:675
#define MERF_TAB
Definition: editstr.h:111
ME_DisplayItem * ME_MakeDI(ME_DIType type) DECLSPEC_HIDDEN
Definition: list.c:178
#define MERF_SPLITMASK
Definition: editstr.h:140
static const char * debugstr_run(const ME_Run *run)
Definition: editor.h:46
ME_DisplayItem * pFirst
Definition: editstr.h:272
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define MERF_ENDWHITE
Definition: editstr.h:122
HRESULT WINAPI ScriptXtoCP(int iX, int cChars, int cGlyphs, const WORD *pwLogClust, const SCRIPT_VISATTR *psva, const int *piAdvance, const SCRIPT_ANALYSIS *psa, int *piCP, int *piTrailing)
Definition: usp10.c:2854
BOOL WINAPI GetTextExtentExPointW(_In_ HDC hdc, _In_reads_(cchString) LPCWSTR lpszString, _In_ int cchString, _In_ int nMaxExtent, _Out_opt_ LPINT lpnFit, _Out_writes_to_opt_(cchString, *lpnFit) LPINT lpnDx, _Out_ LPSIZE lpSize)
struct tagME_DisplayItem * next
Definition: editstr.h:261
#define MERF_HIDDEN
Definition: editstr.h:130
static const WCHAR szText[]
Definition: dialog.c:139
void select_style(ME_Context *c, ME_Style *s) DECLSPEC_HIDDEN
Definition: style.c:369
unsigned int BOOL
Definition: ntddk_ex.h:94
void ME_PropagateCharOffset(ME_DisplayItem *p, int shift)
Definition: run.c:59
void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor)
Definition: run.c:171
ME_DisplayItem * ME_MakeRun(ME_Style *s, int nFlags)
Definition: run.c:296
void ME_DestroyDisplayItem(ME_DisplayItem *item) DECLSPEC_HIDDEN
Definition: list.c:160
#define MERF_ENDPARA
Definition: editstr.h:126
ME_TextBuffer * pBuffer
Definition: editstr.h:390
const WCHAR * str
ME_DisplayItem * ME_SplitRunSimple(ME_TextEditor *editor, ME_Cursor *cursor)
Definition: run.c:258
#define CFM_COLOR
Definition: richedit.h:361
smooth NULL
Definition: ftsmooth.c:416
void ME_GetOLEObjectSize(const ME_Context *c, ME_Run *run, SIZE *pSize) DECLSPEC_HIDDEN
Definition: richole.c:5727
DWORD dwEffects
Definition: richedit.h:307
int ME_GetSelection(ME_TextEditor *editor, ME_Cursor **from, ME_Cursor **to)
Definition: caret.c:57
Definition: editstr.h:91
#define CFM_UNDERLINE
Definition: richedit.h:334
LONG cx
Definition: windef.h:334
int len
Definition: editstr.h:167
void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC)
Definition: context.c:23
void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p)
Definition: run.c:229
#define PFE_TABLE
Definition: richedit.h:944
int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run, BOOL closest, BOOL visual_order)
Definition: run.c:521
DWORD dwMask
Definition: richedit.h:667
#define ITextHost_TxGetDC(This)
Definition: editor.h:287
#define MERF_STYLEFLAGS
Definition: editstr.h:107
void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
Definition: run.c:790
#define TRACE_(x)
Definition: compat.h:66
static BOOL run_is_entirely_ws(const ME_Run *run)
Definition: run.c:395
ME_Style * pDefaultStyle
Definition: editstr.h:274
ME_Style * ME_ApplyStyle(ME_TextEditor *ed, ME_Style *sSrc, CHARFORMAT2W *style) DECLSPEC_HIDDEN
Definition: style.c:156
#define TRACE(s)
Definition: solgame.cpp:4
GLsizeiptr size
Definition: glext.h:5919
BOOL ME_CanJoinRuns(const ME_Run *run1, const ME_Run *run2)
Definition: run.c:35
void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppPara, ME_DisplayItem **ppRun, int *pOfs)
Definition: run.c:184
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define MERF_WHITESPACE
Definition: editstr.h:124
static __inline tree_data * next_item(tree *t, tree_data *td)
Definition: treefuncs.c:334
#define MERF_SPLITTABLE
Definition: editstr.h:118
BOOL ME_InsertString(ME_String *s, int ofs, const WCHAR *insert, int len) DECLSPEC_HIDDEN
Definition: string.c:103
void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat) DECLSPEC_HIDDEN
Definition: list.c:26
const GLubyte * c
Definition: glext.h:8905
void ME_ReleaseStyle(ME_Style *item) DECLSPEC_HIDDEN
Definition: style.c:462
unsigned long DWORD
Definition: ntddk_ex.h:95
#define CFM_SIZE
Definition: richedit.h:362
void ME_AddRefStyle(ME_Style *item) DECLSPEC_HIDDEN
Definition: style.c:454
ME_DisplayItem * ME_InsertRunAtCursor(ME_TextEditor *editor, ME_Cursor *cursor, ME_Style *style, const WCHAR *str, int len, int flags)
Definition: run.c:325
ME_String * ME_MakeStringR(WCHAR cRepeat, int nMaxChars) DECLSPEC_HIDDEN
Definition: string.c:85
ME_DisplayItem * pPara
Definition: editstr.h:279
void ME_SetCharFormat(ME_TextEditor *editor, ME_Cursor *start, ME_Cursor *end, CHARFORMAT2W *pFmt)
Definition: run.c:725
void ME_Remove(ME_DisplayItem *diWhere) DECLSPEC_HIDDEN
Definition: list.c:35
void ME_DestroyContext(ME_Context *c)
Definition: context.c:44
GLbitfield flags
Definition: glext.h:7161
void ME_GetCharFormat(ME_TextEditor *editor, const ME_Cursor *from, const ME_Cursor *to, CHARFORMAT2W *pFmt)
Definition: run.c:819
int nFlags
Definition: editstr.h:169
ME_DisplayItem * ME_GetParagraph(ME_DisplayItem *run) DECLSPEC_HIDDEN
Definition: para.c:815
WCHAR szFaceName[LF_FACESIZE]
Definition: richedit.h:313
int ret
ITextHost * texthost
Definition: editstr.h:387
#define PFM_TABLE
Definition: richedit.h:870
int nWidth
Definition: editstr.h:168
int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset, BOOL visual_order)
Definition: run.c:598
int nOffset
Definition: editstr.h:281
#define MERF_ENDCELL
Definition: editstr.h:113
void ME_CopyCharFormat(CHARFORMAT2W *pDest, const CHARFORMAT2W *pSrc) DECLSPEC_HIDDEN
Definition: style.c:230
static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size)
Definition: run.c:537
GLenum GLsizei len
Definition: glext.h:6722
Definition: editstr.h:90
#define MERF_TABLESTART
Definition: editstr.h:132
GLdouble s
Definition: gl.h:2039
static WCHAR * get_text(const ME_Run *run, int offset)
Definition: editor.h:41
static const WCHAR spaceW[]
Definition: mxwriter.c:44
BOOL add_undo_delete_run(ME_TextEditor *, int pos, int len) DECLSPEC_HIDDEN
Definition: undo.c:204
#define CFM_LINK
Definition: richedit.h:337
#define CFM_STRIKEOUT
Definition: richedit.h:335
POINT pt
Definition: editstr.h:171
COLORREF crTextColor
Definition: richedit.h:310
void ME_DestroyString(ME_String *s) DECLSPEC_HIDDEN
Definition: string.c:96
static BOOL run_is_splittable(const ME_Run *run)
Definition: run.c:377
int * advances
Definition: editstr.h:178
WORD wEffects
Definition: richedit.h:669
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
static ATOM item
Definition: dde.c:856
const char cursor[]
Definition: icontest.c:13
#define MERF_NOJOIN
Definition: editstr.h:135
ME_Cursor * pCursors
Definition: editstr.h:391
static int ME_IsWSpace(WCHAR ch)
Definition: editor.h:101
#define CFM_BOLD
Definition: richedit.h:332
PARAFORMAT2 fmt
Definition: editstr.h:208
struct para_num para_num
Definition: editstr.h:219
GLuint start
Definition: gl.h:1545
int num_glyphs
Definition: editstr.h:175
DWORD dwMask
Definition: richedit.h:306
int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order)
Definition: run.c:462
#define min(a, b)
Definition: monoChain.cc:55
struct tagME_DisplayItem * next_para
Definition: editstr.h:221
ME_DisplayItem * pRun
Definition: editstr.h:280
WORD fLogicalOrder
Definition: usp10.h:144
SCRIPT_ANALYSIS script_analysis
Definition: editstr.h:174
BOOL ME_IsSelection(ME_TextEditor *editor)
Definition: caret.c:1533
_Out_opt_ int * cx
Definition: commctrl.h:581
void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
Definition: run.c:413
POINT cp
Definition: magnifier.c:59
void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
Definition: run.c:801
SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen, int startx, int *pAscent, int *pDescent)
Definition: run.c:616
#define c
Definition: ke_i.h:80
#define MEPF_COMPLEX
Definition: editstr.h:150
ME_DisplayItem * ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass) DECLSPEC_HIDDEN
Definition: list.c:134
void ME_CheckCharOffsets(ME_TextEditor *editor)
Definition: run.c:101
GLfloat GLfloat p
Definition: glext.h:8902
#define lDefaultTab
Definition: richedit.h:215
void mark_para_rewrap(ME_TextEditor *editor, ME_DisplayItem *para) DECLSPEC_HIDDEN
Definition: para.c:26
CardRegion * from
Definition: spigame.cpp:19
LONG dxOffset
Definition: richedit.h:672
Arabic default style
Definition: afstyles.h:93
#define CFM_UNDERLINETYPE
Definition: richedit.h:355
LONG yHeight
Definition: richedit.h:308
#define TRACE_ON(x)
Definition: compat.h:65
SCRIPT_VISATTR * vis_attrs
Definition: editstr.h:177
CHARFORMAT2W fmt
Definition: editstr.h:77
BOOL WINAPI GetTextExtentPoint32W(_In_ HDC hdc, _In_reads_(c) LPCWSTR lpString, _In_ int c, _Out_ LPSIZE psizl)
int ME_GetTextLength(ME_TextEditor *editor)
Definition: caret.c:83
ME_DisplayItem * ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass) DECLSPEC_HIDDEN
Definition: list.c:111
union tagME_DisplayItem::@521 member
BYTE bPitchAndFamily
Definition: richedit.h:312
int ME_twips2pointsX(const ME_Context *c, int x) DECLSPEC_HIDDEN
Definition: paint.c:153