ReactOS  0.4.13-dev-563-g0561610
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  HGDIOBJ hOldFont;
468  SIZE sz, sz2, sz3;
469  if (!run->len || cx <= 0)
470  return 0;
471 
472  if (run->nFlags & (MERF_TAB | MERF_ENDCELL))
473  {
474  if (!closest || cx < run->nWidth / 2) return 0;
475  return 1;
476  }
477 
478  if (run->nFlags & MERF_GRAPHICS)
479  {
480  SIZE sz;
481  ME_GetOLEObjectSize(c, run, &sz);
482  if (!closest || cx < sz.cx / 2) return 0;
483  return 1;
484  }
485 
486  if (run->para->nFlags & MEPF_COMPLEX)
487  {
488  int cp, trailing;
489  if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1;
490 
491  ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis,
492  &cp, &trailing );
493  TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth,
495  return closest ? cp + trailing : cp;
496  }
497 
498  if (c->editor->cPasswordMask)
499  {
500  mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len );
501  str = mask_text->szData;
502  }
503  else
504  str = get_text( run, 0 );
505 
506  hOldFont = ME_SelectStyleFont(c, run->style);
507  GetTextExtentExPointW(c->hDC, str, run->len,
508  cx, &fit, NULL, &sz);
509  if (closest && fit != run->len)
510  {
511  GetTextExtentPoint32W(c->hDC, str, fit, &sz2);
512  GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3);
513  if (cx >= (sz2.cx+sz3.cx)/2)
514  fit = fit + 1;
515  }
516 
517  ME_DestroyString( mask_text );
518 
519  ME_UnselectStyleFont(c, run->style, hOldFont);
520  return fit;
521 }
522 
523 int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run, BOOL closest, BOOL visual_order)
524 {
525  ME_Context c;
526  int ret;
527 
528  ME_InitContext( &c, editor, ITextHost_TxGetDC( editor->texthost ) );
529  ret = ME_CharFromPointContext( &c, cx, run, closest, visual_order );
531  return ret;
532 }
533 
534 /******************************************************************************
535  * ME_GetTextExtent
536  *
537  * Finds a width and a height of the text using a specified style
538  */
539 static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size)
540 {
541  HGDIOBJ hOldFont;
542  if (c->hDC) {
543  hOldFont = ME_SelectStyleFont(c, s);
544  GetTextExtentPoint32W(c->hDC, szText, nChars, size);
545  ME_UnselectStyleFont(c, s, hOldFont);
546  } else {
547  size->cx = 0;
548  size->cy = 0;
549  }
550 }
551 
552 /******************************************************************************
553  * ME_PointFromCharContext
554  *
555  * Returns a run-relative pixel position given a run-relative character
556  * position (character offset)
557  */
558 int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visual_order)
559 {
560  SIZE size;
561  ME_String *mask_text = NULL;
562  WCHAR *str;
563 
564  if (pRun->nFlags & MERF_GRAPHICS)
565  {
566  if (nOffset)
567  ME_GetOLEObjectSize(c, pRun, &size);
568  return nOffset != 0;
569  } else if (pRun->nFlags & MERF_ENDPARA) {
570  nOffset = 0;
571  }
572 
573  if (pRun->para->nFlags & MEPF_COMPLEX)
574  {
575  int x;
576  ScriptCPtoX( nOffset, FALSE, pRun->len, pRun->num_glyphs, pRun->clusters,
577  pRun->vis_attrs, pRun->advances, &pRun->script_analysis, &x );
578  if (visual_order && pRun->script_analysis.fRTL) x = pRun->nWidth - x - 1;
579  return x;
580  }
581  if (c->editor->cPasswordMask)
582  {
583  mask_text = ME_MakeStringR(c->editor->cPasswordMask, pRun->len);
584  str = mask_text->szData;
585  }
586  else
587  str = get_text( pRun, 0 );
588 
589  ME_GetTextExtent(c, str, nOffset, pRun->style, &size);
590  ME_DestroyString( mask_text );
591  return size.cx;
592 }
593 
594 /******************************************************************************
595  * ME_PointFromChar
596  *
597  * Calls ME_PointFromCharContext after first creating a context.
598  */
599 int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset, BOOL visual_order)
600 {
601  ME_Context c;
602  int ret;
603 
604  ME_InitContext(&c, editor, ITextHost_TxGetDC(editor->texthost));
605  ret = ME_PointFromCharContext( &c, pRun, nOffset, visual_order );
607 
608  return ret;
609 }
610 
611 /******************************************************************************
612  * ME_GetRunSizeCommon
613  *
614  * Finds width, height, ascent and descent of a run, up to given character
615  * (nLen).
616  */
617 SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen,
618  int startx, int *pAscent, int *pDescent)
619 {
620  static const WCHAR spaceW[] = {' ',0};
621  SIZE size;
622 
623  nLen = min( nLen, run->len );
624 
625  if (run->nFlags & MERF_ENDPARA)
626  {
627  nLen = min( nLen, 1 );
628  ME_GetTextExtent(c, spaceW, nLen, run->style, &size);
629  }
630  else if (para->nFlags & MEPF_COMPLEX)
631  {
632  size.cx = run->nWidth;
633  }
634  else if (c->editor->cPasswordMask)
635  {
636  ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen);
637  ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size);
638  ME_DestroyString(szMasked);
639  }
640  else
641  {
642  ME_GetTextExtent(c, get_text( run, 0 ), nLen, run->style, &size);
643  }
644  *pAscent = run->style->tm.tmAscent;
645  *pDescent = run->style->tm.tmDescent;
646  size.cy = *pAscent + *pDescent;
647 
648  if (run->nFlags & MERF_TAB)
649  {
650  int pos = 0, i = 0, ppos, shift = 0;
651  const PARAFORMAT2 *pFmt = &para->fmt;
652 
653  if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */
654  pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE)
655  /* The horizontal gap shifts the tab positions to leave the gap. */
656  shift = pFmt->dxOffset * 2;
657  do {
658  if (i < pFmt->cTabCount)
659  {
660  /* Only one side of the horizontal gap is needed at the end of
661  * the table row. */
662  if (i == pFmt->cTabCount -1)
663  shift = shift >> 1;
664  pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF);
665  i++;
666  }
667  else
668  {
669  pos += lDefaultTab - (pos % lDefaultTab);
670  }
671  ppos = ME_twips2pointsX(c, pos);
672  if (ppos > startx + run->pt.x) {
673  size.cx = ppos - startx - run->pt.x;
674  break;
675  }
676  } while(1);
677  size.cy = *pAscent + *pDescent;
678  return size;
679  }
680  if (run->nFlags & MERF_GRAPHICS)
681  {
682  ME_GetOLEObjectSize(c, run, &size);
683  if (size.cy > *pAscent)
684  *pAscent = size.cy;
685  /* descent is unchanged */
686  return size;
687  }
688  return size;
689 }
690 
691 /******************************************************************************
692  * ME_SetSelectionCharFormat
693  *
694  * Applies a style change, either to a current selection, or to insert cursor
695  * (ie. the style next typed characters will use).
696  */
698 {
699  if (!ME_IsSelection(editor))
700  {
701  ME_Style *s;
702  if (!editor->pBuffer->pCharStyle)
703  editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0);
704  s = ME_ApplyStyle(editor, editor->pBuffer->pCharStyle, pFmt);
706  editor->pBuffer->pCharStyle = s;
707  } else {
708  ME_Cursor *from, *to;
709  ME_GetSelection(editor, &from, &to);
710  ME_SetCharFormat(editor, from, to, pFmt);
711  }
712 }
713 
714 /******************************************************************************
715  * ME_SetCharFormat
716  *
717  * Applies a style change to the specified part of the text
718  *
719  * The start and end cursors specify the part of the text. These cursors will
720  * be updated to stay valid, but this function may invalidate other
721  * non-selection cursors. The end cursor may be NULL to specify all the text
722  * following the start cursor.
723  *
724  * If no text is selected, then nothing is done.
725  */
727 {
728  ME_DisplayItem *run, *start_run = start->pRun, *end_run = NULL;
729 
730  if (end && start->pRun == end->pRun && start->nOffset == end->nOffset)
731  return;
732 
733  if (start->nOffset == start->pRun->member.run.len)
734  start_run = ME_FindItemFwd( start->pRun, diRun );
735  else if (start->nOffset)
736  {
737  /* SplitRunSimple may or may not update the cursors, depending on whether they
738  * are selection cursors, but we need to make sure they are valid. */
739  int split_offset = start->nOffset;
740  ME_DisplayItem *split_run = ME_SplitRunSimple(editor, start);
741  start_run = start->pRun;
742  if (end && end->pRun == split_run)
743  {
744  end->pRun = start->pRun;
745  end->nOffset -= split_offset;
746  }
747  }
748 
749  if (end)
750  {
751  if (end->nOffset == end->pRun->member.run.len)
752  end_run = ME_FindItemFwd( end->pRun, diRun );
753  else
754  {
755  if (end->nOffset) ME_SplitRunSimple(editor, end);
756  end_run = end->pRun;
757  }
758  }
759 
760  for (run = start_run; run != end_run; run = ME_FindItemFwd( run, diRun ))
761  {
762  ME_Style *new_style = ME_ApplyStyle(editor, run->member.run.style, pFmt);
763  ME_Paragraph *para = run->member.run.para;
764 
765  add_undo_set_char_fmt( editor, run->member.run.para->nCharOfs + run->member.run.nCharOfs,
766  run->member.run.len, &run->member.run.style->fmt );
768  run->member.run.style = new_style;
769 
770  /* The para numbering style depends on the eop style */
771  if ((run->member.run.nFlags & MERF_ENDPARA) && para->para_num.style)
772  {
773  ME_ReleaseStyle(para->para_num.style);
774  para->para_num.style = NULL;
775  }
776  mark_para_rewrap(editor, get_di_from_para(para));
777  }
778 }
779 
781 {
782  ME_CopyCharFormat(pFmt, &run->member.run.style->fmt);
783 }
784 
785 /******************************************************************************
786  * ME_GetDefaultCharFormat
787  *
788  * Retrieves the current default character style (the one applied where no
789  * other style was applied) .
790  */
792 {
793  ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt);
794 }
795 
796 /******************************************************************************
797  * ME_GetSelectionCharFormat
798  *
799  * If selection exists, it returns all style elements that are set consistently
800  * in the whole selection. If not, it just returns the current style.
801  */
803 {
804  ME_Cursor *from, *to;
805  if (!ME_IsSelection(editor) && editor->pBuffer->pCharStyle)
806  {
807  ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt);
808  return;
809  }
810  ME_GetSelection(editor, &from, &to);
811  ME_GetCharFormat(editor, from, to, pFmt);
812 }
813 
814 /******************************************************************************
815  * ME_GetCharFormat
816  *
817  * Returns the style consisting of those attributes which are consistently set
818  * in the whole character range.
819  */
821  const ME_Cursor *to, CHARFORMAT2W *pFmt)
822 {
823  ME_DisplayItem *run, *run_end;
824  CHARFORMAT2W tmp;
825 
826  run = from->pRun;
827  /* special case - if selection is empty, take previous char's formatting */
828  if (from->pRun == to->pRun && from->nOffset == to->nOffset)
829  {
830  if (!from->nOffset)
831  {
833  if (tmp_run->type == diRun) {
834  ME_GetRunCharFormat(editor, tmp_run, pFmt);
835  return;
836  }
837  }
838  ME_GetRunCharFormat(editor, run, pFmt);
839  return;
840  }
841 
842  run_end = to->pRun;
843  if (!to->nOffset)
844  run_end = ME_FindItemBack(run_end, diRun);
845 
846  ME_GetRunCharFormat(editor, run, pFmt);
847 
848  if (run == run_end) return;
849 
850  do {
851  /* FIXME add more style feature comparisons */
854 
855  run = ME_FindItemFwd(run, diRun);
856 
857  ZeroMemory(&tmp, sizeof(tmp));
858  tmp.cbSize = sizeof(tmp);
859  ME_GetRunCharFormat(editor, run, &tmp);
860 
861  assert((tmp.dwMask & dwAttribs) == dwAttribs);
862  /* reset flags that differ */
863 
864  if (pFmt->yHeight != tmp.yHeight)
865  pFmt->dwMask &= ~CFM_SIZE;
866  if (pFmt->dwMask & CFM_FACE)
867  {
868  if (!(tmp.dwMask & CFM_FACE))
869  pFmt->dwMask &= ~CFM_FACE;
870  else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName) ||
871  pFmt->bPitchAndFamily != tmp.bPitchAndFamily)
872  pFmt->dwMask &= ~CFM_FACE;
873  }
874  if (pFmt->yHeight != tmp.yHeight)
875  pFmt->dwMask &= ~CFM_SIZE;
876  if (pFmt->bUnderlineType != tmp.bUnderlineType)
877  pFmt->dwMask &= ~CFM_UNDERLINETYPE;
878  if (pFmt->dwMask & CFM_COLOR)
879  {
880  if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR)))
881  {
882  if (pFmt->crTextColor != tmp.crTextColor)
883  pFmt->dwMask &= ~CFM_COLOR;
884  }
885  }
886 
887  pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & dwEffects);
888  pFmt->dwEffects = tmp.dwEffects;
889 
890  } while(run != run_end);
891 }
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:1668
ME_Style * ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) DECLSPEC_HIDDEN
Definition: style.c:457
HFONT ME_SelectStyleFont(ME_Context *c, ME_Style *s) DECLSPEC_HIDDEN
Definition: style.c:356
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:558
void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
Definition: run.c:697
int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2)
Definition: lstring.c:170
#define MERF_STARTWHITE
Definition: editstr.h:120
static void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt)
Definition: run.c:780
#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:2648
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:1635
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
void ME_UnselectStyleFont(ME_Context *c, ME_Style *s, HFONT hOldFont) DECLSPEC_HIDDEN
Definition: style.c:416
LONG tmDescent
Definition: wingdi.h:2363
LONG tmAscent
Definition: wingdi.h:2362
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:30
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:47
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:2852
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
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:5732
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:319
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:523
DWORD dwMask
Definition: richedit.h:667
#define ITextHost_TxGetDC(This)
Definition: editor.h:288
#define MERF_STYLEFLAGS
Definition: editstr.h:107
void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt)
Definition: run.c:791
#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:152
#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:443
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:435
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:726
void ME_Remove(ME_DisplayItem *diWhere) DECLSPEC_HIDDEN
Definition: list.c:35
void ME_DestroyContext(ME_Context *c)
Definition: context.c:42
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:820
int nFlags
Definition: editstr.h:169
ME_DisplayItem * ME_GetParagraph(ME_DisplayItem *run) DECLSPEC_HIDDEN
Definition: para.c:816
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:599
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:226
static void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, SIZE *size)
Definition: run.c:539
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:42
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
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:103
#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:1521
_Out_opt_ int * cx
Definition: commctrl.h:570
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:802
SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen, int startx, int *pAscent, int *pDescent)
Definition: run.c:617
#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
BYTE bPitchAndFamily
Definition: richedit.h:312
union tagME_DisplayItem::@506 member
int ME_twips2pointsX(const ME_Context *c, int x) DECLSPEC_HIDDEN
Definition: paint.c:153