wrap.c 14.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * RichEdit - Paragraph wrapping. Don't try to understand it. You've been
 * warned !
 *
 * Copyright 2004 by Krzysztof Foltman
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "editor.h"

WINE_DEFAULT_DEBUG_CHANNEL(richedit);

/*
 * Unsolved problems:
29
 *
30 31
 * - center and right align in WordPad omits all spaces at the start, we don't
 * - objects/images are not handled yet
32 33 34
 * - no tabs
 */

35 36 37
ME_DisplayItem *ME_MakeRow(int height, int baseline, int width)
{
  ME_DisplayItem *item = ME_MakeDI(diStartRow);
38

39 40 41 42 43 44
  item->member.row.nHeight = height;
  item->member.row.nBaseline = baseline;
  item->member.row.nWidth = width;
  return item;
}

45
static void ME_BeginRow(ME_WrapContext *wc)
46 47 48 49 50 51
{
  wc->pRowStart = NULL;
  wc->bOverflown = FALSE;
  wc->pLastSplittableRun = NULL;
  wc->nAvailWidth = wc->nTotalWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
  wc->pt.x = 0;
52
}
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd)
{
  ME_DisplayItem *p, *row, *para;
  int ascent = 0, descent = 0, width=0, shift = 0, align = 0;
  /* wrap text */
  para = ME_GetParagraph(wc->pRowStart);
  for (p = wc->pRowStart; p!=pEnd; p = p->next)
  {
    /* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */
    if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */
      if (p->member.run.nAscent>ascent)
        ascent = p->member.run.nAscent;
      if (p->member.run.nDescent>descent)
        descent = p->member.run.nDescent;
      if (!(p->member.run.nFlags & (MERF_ENDPARA|MERF_SKIPPED)))
        width += p->member.run.nWidth;
    }
  }
  row = ME_MakeRow(ascent+descent, ascent, width);
  row->member.row.nYPos = wc->pt.y;
  row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin);
  row->member.row.nRMargin = wc->nRightMargin;
  assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT);
  align = para->member.para.pFmt->wAlignment;
  if (align == PFA_CENTER)
    shift = (wc->nAvailWidth-width)/2;
  if (align == PFA_RIGHT)
    shift = wc->nAvailWidth-width;
  for (p = wc->pRowStart; p!=pEnd; p = p->next)
  {
    if (p->type==diRun) { /* FIXME add more run types */
      p->member.run.pt.x += row->member.row.nLMargin+shift;
    }
  }
  ME_InsertBefore(wc->pRowStart, row);
  wc->nRow++;
  wc->pt.y += ascent+descent;
  ME_BeginRow(wc);
}

94
static void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p)
95 96 97
{
  if (wc->pRowStart)
    ME_InsertRowStart(wc, p->next);
98

99 100 101 102 103 104 105 106 107 108 109 110 111
  /*
  p = p->member.para.prev_para->next;
  while(p) {
    if (p->type == diParagraph || p->type == diTextEnd)
      return;
    if (p->type == diRun)
    {
      ME_Run *run = &p->member.run;
      TRACE("%s - (%d, %d)\n", debugstr_w(run->strText->szData), run->pt.x, run->pt.y);
    }
    p = p->next;
  }
  */
112
}
113

114
static void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p)
115 116
{
  /* FIXME compose style (out of character and paragraph styles) here */
117

118
  ME_UpdateRunFlags(wc->context->editor, &p->member.run);
119 120

  ME_CalcRunExtent(wc->context, &ME_GetParagraph(p)->member.para, &p->member.run);
121 122
}

123
static ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i)
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
{
  ME_DisplayItem *pp, *piter = p;
  int j;
  if (!i)
    return NULL;
  j = ME_ReverseFindNonWhitespaceV(p->member.run.strText, i);
  if (j>0) {
    pp = ME_SplitRun(wc->context, piter, j);
    wc->pt.x += piter->member.run.nWidth;
    return pp;
  }
  else
  {
    pp = piter;
    /* omit all spaces before split point */
    while(piter != wc->pRowStart)
    {
      piter = ME_FindItemBack(piter, diRun);
142
      if (piter->member.run.nFlags & MERF_WHITESPACE)
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
      {
        pp = piter;
        continue;
      }
      if (piter->member.run.nFlags & MERF_ENDWHITE)
      {
        j = ME_ReverseFindNonWhitespaceV(piter->member.run.strText, i);
        pp = ME_SplitRun(wc->context, piter, i);
        wc->pt = pp->member.run.pt;
        return pp;
      }
      /* this run is the end of spaces, so the run edge is a good point to split */
      wc->pt = pp->member.run.pt;
      wc->bOverflown = TRUE;
      TRACE("Split point is: %s|%s\n", debugstr_w(piter->member.run.strText->szData), debugstr_w(pp->member.run.strText->szData));
      return pp;
    }
    wc->pt = piter->member.run.pt;
    return piter;
  }
}

165
static ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc)
166 167 168 169
{
  ME_DisplayItem *piter = p, *pp;
  int i, idesp, len;
  ME_Run *run = &p->member.run;
170 171

  idesp = i = ME_CharFromPoint(wc->context->editor, loc, &ME_GetParagraph(p)->member.para, run);
172 173 174 175 176 177 178 179 180 181 182 183 184
  len = ME_StrVLen(run->strText);
  assert(len>0);
  assert(i<len);
  if (i) {
    /* don't split words */
    i = ME_ReverseFindWhitespaceV(run->strText, i);
    pp = ME_MaximizeSplit(wc, p, i);
    if (pp)
      return pp;
  }
  TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData));
  if (wc->pLastSplittableRun)
  {
185
    if (wc->pLastSplittableRun->member.run.nFlags & (MERF_GRAPHICS|MERF_TAB))
186 187 188 189 190 191 192 193 194 195
    {
      wc->pt = wc->ptLastSplittableRun;
      return wc->pLastSplittableRun;
    }
    else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)
    {
      /* the following two lines are just to check if we forgot to call UpdateRunFlags earlier,
         they serve no other purpose */
      ME_UpdateRunFlags(wc->context->editor, run);
      assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE));
196

197 198 199 200 201 202 203
      piter = wc->pLastSplittableRun;
      run = &piter->member.run;
      len = ME_StrVLen(run->strText);
      /* don't split words */
      i = ME_ReverseFindWhitespaceV(run->strText, len);
      if (i == len)
        i = ME_ReverseFindNonWhitespaceV(run->strText, len);
204
      if (i) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        ME_DisplayItem *piter2 = ME_SplitRun(wc->context, piter, i);
        wc->pt = piter2->member.run.pt;
        return piter2;
      }
      /* splittable = must have whitespaces */
      assert(0 == "Splittable, but no whitespaces");
    }
    else
    {
      /* restart from the first run beginning with spaces */
      wc->pt = wc->ptLastSplittableRun;
      return wc->pLastSplittableRun;
    }
  }
  TRACE("Backtracking failed, trying desperate: %s\n", debugstr_w(p->member.run.strText->szData));
  /* OK, no better idea, so assume we MAY split words if we can split at all*/
  if (idesp)
    return ME_SplitRun(wc->context, piter, idesp);
  else
  if (wc->pRowStart && piter != wc->pRowStart)
  {
    /* don't need to break current run, because it's possible to split
       before this run */
    wc->bOverflown = TRUE;
    return piter;
  }
  else
  {
    /* split point inside first character - no choice but split after that char */
    int chars = 1;
    int pos2 = ME_StrRelPos(run->strText, 0, &chars);
    if (pos2 != len) {
      /* the run is more than 1 char, so we may split */
      return ME_SplitRun(wc->context, piter, pos2);
    }
    /* the run is one char, can't split it */
    return piter;
242
  }
243 244
}

245
static ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
246 247 248 249 250
{
  ME_DisplayItem *pp;
  ME_Run *run;
  int len;

251
  assert(p->type == diRun);
252 253 254 255 256
  if (!wc->pRowStart)
    wc->pRowStart = p;
  run = &p->member.run;
  run->pt.x = wc->pt.x;
  run->pt.y = wc->pt.y;
257 258 259
  ME_WrapSizeRun(wc, p);
  len = ME_StrVLen(run->strText);

260
  if (wc->bOverflown) /* just skipping final whitespaces */
261 262
  {
    if (run->nFlags & (MERF_WHITESPACE|MERF_TAB)) {
263 264 265 266 267
      p->member.run.nFlags |= MERF_SKIPPED;
      /* wc->pt.x += run->nWidth; */
      /* skip runs consisting of only whitespaces */
      return p->next;
    }
268

269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    if (run->nFlags & MERF_STARTWHITE) {
      /* try to split the run at the first non-white char */
      int black;
      black = ME_FindNonWhitespaceV(run->strText, 0);
      if (black) {
        wc->bOverflown = FALSE;
        pp = ME_SplitRun(wc->context, p, black);
        p->member.run.nFlags |= MERF_SKIPPED;
        ME_InsertRowStart(wc, pp);
        return pp;
      }
    }
    /* black run: the row goes from pRowStart to the previous run */
    ME_InsertRowStart(wc, p);
    return p;
  }
  /* we're not at the end of the row */
  /* will current run fit? */
  if (wc->pt.x + run->nWidth > wc->nAvailWidth)
  {
289
    int loc = wc->nAvailWidth - wc->pt.x;
290 291 292 293 294 295
    /* total white run ? */
    if (run->nFlags & MERF_WHITESPACE) {
      /* let the overflow logic handle it */
      wc->bOverflown = TRUE;
      return p;
    }
296 297
    /* graphics or TAB - we can split before */
    if (run->nFlags & (MERF_GRAPHICS|MERF_TAB)) {
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      wc->bOverflown = TRUE;
      return p;
    }
    /* can we separate out the last spaces ? (to use overflow logic later) */
    if (run->nFlags & MERF_ENDWHITE)
    {
      /* we aren't sure if it's *really* necessary, it's a good start however */
      int black = ME_ReverseFindNonWhitespaceV(run->strText, len);
      ME_SplitRun(wc->context, p, black);
      /* handle both parts again */
      return p;
    }
    /* determine the split point by backtracking */
    pp = ME_SplitByBacktracking(wc, p, loc);
    if (pp == wc->pRowStart)
    {
      /* we had only spaces so far, entire content can be omitted */
      wc->pt.x = 0;
      return p->next;
    }
    if (p != pp) /* found a suitable split point */
    {
      wc->bOverflown = TRUE;
      return pp;
    }
    /* we detected that it's best to split on start of this run */
    if (wc->bOverflown)
      return pp;
    ERR("failure!\n");
    /* not found anything - writing over margins is the only option left */
  }
329 330
  if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
    || ((run->nFlags & (MERF_GRAPHICS|MERF_TAB)) && (p != wc->pRowStart)))
331 332 333 334 335 336 337
  {
    wc->pLastSplittableRun = p;
    wc->ptLastSplittableRun = wc->pt;
  }
  wc->pt.x += run->nWidth;
  return p->next;
}
338

339 340 341
void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
  ME_DisplayItem *p;
  ME_WrapContext wc;
342
  int dpi = GetDeviceCaps(c->hDC, LOGPIXELSX);
343

344
  assert(tp->type == diParagraph);
345
  if (!(tp->member.para.nFlags & MEPF_REWRAP)) {
346 347 348
    return;
  }
  ME_PrepareParagraphForWrapping(c, tp);
349

350 351 352
  wc.context = c;
/*   wc.para_style = tp->member.para.style; */
  wc.style = NULL;
353 354 355
  tp->member.para.nRightMargin = tp->member.para.pFmt->dxRightIndent*dpi/1440;
  tp->member.para.nFirstMargin = tp->member.para.pFmt->dxStartIndent*dpi/1440;
  tp->member.para.nLeftMargin = (tp->member.para.pFmt->dxStartIndent+tp->member.para.pFmt->dxOffset)*dpi/1440;
356 357 358 359 360 361 362 363 364
  wc.nFirstMargin = tp->member.para.nFirstMargin;
  wc.nLeftMargin = tp->member.para.nLeftMargin;
  wc.nRightMargin = tp->member.para.nRightMargin;
  wc.nRow = 0;
  wc.pt.x = 0;
  wc.pt.y = 0;
  wc.nTotalWidth = c->rcView.right - c->rcView.left;
  wc.nAvailWidth = wc.nTotalWidth - wc.nFirstMargin - wc.nRightMargin;
  wc.pRowStart = NULL;
365

366 367 368 369 370 371 372 373 374 375
  ME_BeginRow(&wc);
  for (p = tp->next; p!=tp->member.para.next_para; ) {
    assert(p->type != diStartRow);
    if (p->type == diRun) {
      p = ME_WrapHandleRun(&wc, p);
      continue;
    }
    p = p->next;
  }
  ME_WrapEndParagraph(&wc, p);
376
  tp->member.para.nFlags &= ~MEPF_REWRAP;
377
  tp->member.para.nHeight = wc.pt.y;
378
  tp->member.para.nRows = wc.nRow;
379 380 381 382
}


void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) {
383
  ME_DisplayItem *p, *pRow;
384

385
  /* remove all items that will be reinserted by paragraph wrapper anyway */
386
  tp->member.para.nRows = 0;
387 388 389
  for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
    switch(p->type) {
      case diStartRow:
390
        pRow = p;
391
        p = p->prev;
392 393
        ME_Remove(pRow);
        ME_DestroyDisplayItem(pRow);
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
        break;
      default:
        break;
    }
  }
  /* join runs that can be joined, set up flags */
  for (p = tp->next; p!=tp->member.para.next_para; p = p->next) {
    int changed = 0;
    switch(p->type) {
      case diStartRow: assert(0); break; /* should have deleted it */
      case diRun:
        while (p->next->type == diRun) { /* FIXME */
          if (ME_CanJoinRuns(&p->member.run, &p->next->member.run)) {
            ME_JoinRuns(c->editor, p);
            changed = 1;
          }
          else
            break;
        }
        p->member.run.nFlags &= ~MERF_CALCBYWRAP;
        break;
      default:
        break;
    }
  }
}
420

421
BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
422 423 424 425
  HWND hWnd = editor->hWnd;
  HDC hDC = GetDC(hWnd);
  ME_DisplayItem *item;
  ME_Context c;
426
  BOOL bModified = FALSE;
427
  int yStart = -1, yEnd = -1;
428

429 430 431 432 433 434
  ME_InitContext(&c, editor, hDC);
  c.pt.x = 0;
  c.pt.y = 0;
  item = editor->pBuffer->pFirst->next;
  while(item != editor->pBuffer->pLast) {
    BOOL bRedraw = FALSE;
435

436 437 438 439 440
    assert(item->type == diParagraph);
    if ((item->member.para.nFlags & MEPF_REWRAP)
     || (item->member.para.nYPos != c.pt.y))
      bRedraw = TRUE;
    item->member.para.nYPos = c.pt.y;
441

442 443 444
    ME_WrapTextParagraph(&c, item);

    if (bRedraw)
445
    {
446
      item->member.para.nFlags |= MEPF_REPAINT;
447 448 449
      if (yStart == -1)
        yStart = c.pt.y;
    }
450

451 452
    bModified = bModified | bRedraw;

453
    c.pt.y += item->member.para.nHeight;
454 455
    if (bRedraw)
      yEnd = c.pt.y;
456 457 458 459
    item = item->member.para.next_para;
  }
  editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
  editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
460
  
461
  editor->nTotalLength = c.pt.y;
462

463 464
  ME_DestroyContext(&c);
  ReleaseDC(hWnd, hDC);
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

  if (editor->bRedraw)
  {
     RECT rc = c.rcView;
     
     /* Invalidate rewrapped rows */
     if (yStart != -1)
     {
       yStart -= ME_GetYScrollPos(editor);
       yEnd -= ME_GetYScrollPos(editor);
       if ((yStart >= 0 && yStart < c.rcView.bottom - c.rcView.top)
           || (yEnd >= 0 && yEnd < c.rcView.bottom - c.rcView.top))
       {
         rc.top = yStart;
         rc.bottom = yEnd;
         InvalidateRect(editor->hWnd, &rc, TRUE);
       }
     }

     /* Invalidate cursor row */
     if (editor->nInvalidOfs != -1)
     {
       ME_InvalidateFromOfs(editor, editor->nInvalidOfs);
       editor->nInvalidOfs = -1;
     }
  }
491
  return bModified;
492
}
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518


void
ME_SendRequestResize(ME_TextEditor *editor, BOOL force)
{
  if (editor->nEventMask & ENM_REQUESTRESIZE)
  {
    RECT rc;

    GetClientRect(editor->hWnd, &rc);

    if (force || rc.bottom != editor->nTotalLength)
    {
      REQRESIZE info;

      info.nmhdr.hwndFrom = editor->hWnd;
      info.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID);
      info.nmhdr.code = EN_REQUESTRESIZE;
      info.rc = rc;
      info.rc.bottom = editor->nTotalLength;
    
      SendMessageW(GetParent(editor->hWnd), WM_NOTIFY,
                   info.nmhdr.idFrom, (LPARAM)&info);
    }
  }
}