para.c 30.3 KB
Newer Older
1 2 3 4
/*
 * RichEdit - functions working on paragraphs of text (diParagraph).
 * 
 * Copyright 2004 by Krzysztof Foltman
5
 * Copyright 2006 by Phil Krylov
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24 25
 */ 

#include "editor.h"

WINE_DEFAULT_DEBUG_CHANNEL(richedit);

26
void para_mark_rewrap( ME_TextEditor *editor, ME_Paragraph *para )
27
{
28 29
    para->nFlags |= MEPF_REWRAP;
    para_mark_add( editor, para );
30 31
}

32
static ME_Paragraph *para_create( ME_TextEditor *editor )
33 34 35
{
    ME_DisplayItem *item = ME_MakeDI(diParagraph);

36
    editor_set_default_para_fmt( editor, &item->member.para.fmt );
37
    item->member.para.nFlags = MEPF_REWRAP;
38

39
    return &item->member.para;
40 41
}

42
void para_destroy( ME_TextEditor *editor, ME_Paragraph *para )
43
{
44
    if (para->nWidth == editor->nTotalWidth)
45
    {
46
        para->nWidth = 0;
47 48
        editor->nTotalWidth = get_total_width(editor);
    }
49 50 51 52 53
    editor->total_rows -= para->nRows;
    ME_DestroyString( para->text );
    para_num_clear( &para->para_num );
    para_mark_remove( editor, para );
    ME_DestroyDisplayItem( para_get_di( para ) );
54 55
}

56 57 58 59 60 61 62 63 64
/* Note para_next/prev will return the start and end doc nodes */
ME_Paragraph *para_next( ME_Paragraph *para )
{
    if (para->next_para) return &para->next_para->member.para;
    return NULL;
}

ME_Paragraph *para_prev( ME_Paragraph *para )
{
65
    if (para->prev_para && para->prev_para->type == diParagraph) return &para->prev_para->member.para;
66 67 68
    return NULL;
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
int get_total_width(ME_TextEditor *editor)
{
    ME_Paragraph *para;
    int total_width = 0;

    if (editor->pBuffer->pFirst && editor->pBuffer->pLast)
    {
        para = &editor->pBuffer->pFirst->next->member.para;
        while (para != &editor->pBuffer->pLast->member.para && para->next_para)
        {
            total_width = max(total_width, para->nWidth);
            para = &para->next_para->member.para;
        }
    }

    return total_width;
}

87
static int para_mark_compare( const void *key, const struct wine_rb_entry *entry )
88
{
89
    ME_Paragraph *para = WINE_RB_ENTRY_VALUE( entry, ME_Paragraph, marked_entry );
90

91
    return *(int *)key - para->nCharOfs;
92 93
}

94
void para_mark_remove( ME_TextEditor *editor, ME_Paragraph *para )
95
{
96 97
    wine_rb_remove_key( &editor->marked_paras, &para->nCharOfs );
}
98

99 100 101
void para_mark_add( ME_TextEditor *editor, ME_Paragraph *para )
{
    wine_rb_put( &editor->marked_paras, &para->nCharOfs, &para->marked_entry );
102 103
}

104 105 106 107 108 109 110 111 112 113 114 115 116
ME_Run *para_first_run( ME_Paragraph *para )
{
    ME_DisplayItem *di;

    for (di = para_get_di( para ); di != para->next_para; di = di->next )
    {
        if (di->type != diRun) continue;
        return &di->member.run;
    }
    ERR( "failed to find run in paragraph\n" );
    return NULL;
}

117 118 119 120 121
ME_Run *para_end_run( ME_Paragraph *para )
{
    return para->eop_run;
}

122 123 124 125 126
BOOL para_in_table( ME_Paragraph *para )
{
    return para->fmt.wEffects & PFE_TABLE;
}

127 128
ME_Cell *para_cell( ME_Paragraph *para )
{
129
    return para->cell;
130 131
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
ME_Row *para_first_row( ME_Paragraph *para )
{
    ME_DisplayItem *item;

    item = ME_FindItemFwd( para_get_di( para ), diStartRowOrParagraph );
    if (!item || item->type != diStartRow) return NULL;
    return &item->member.row;
}

ME_Row *para_end_row( ME_Paragraph *para )
{
    ME_DisplayItem *item;

    para = para_next( para );
    item = ME_FindItemBack( para_get_di( para ), diStartRowOrParagraph );
    if (!item || item->type != diStartRow) return NULL;
    return &item->member.row;
}

151
void ME_MakeFirstParagraph(ME_TextEditor *editor)
152
{
153
  ME_Context c;
154
  CHARFORMAT2W cf;
155
  const CHARFORMATW *host_cf;
156 157
  LOGFONTW lf;
  HFONT hf;
158
  ME_TextBuffer *text = editor->pBuffer;
159
  ME_Paragraph *para = para_create( editor );
160
  ME_Run *run;
161
  ME_Style *style;
162
  int eol_len;
163
  HDC hdc = ITextHost_TxGetDC( editor->texthost );
164

165
  ME_InitContext( &c, editor, hdc );
166

167
  hf = GetStockObject(SYSTEM_FONT);
168 169 170 171
  assert(hf);
  GetObjectW(hf, sizeof(LOGFONTW), &lf);
  ZeroMemory(&cf, sizeof(cf));
  cf.cbSize = sizeof(cf);
172 173
  cf.dwMask = CFM_ANIMATION|CFM_BACKCOLOR|CFM_CHARSET|CFM_COLOR|CFM_FACE|CFM_KERNING|CFM_LCID|CFM_OFFSET;
  cf.dwMask |= CFM_REVAUTHOR|CFM_SIZE|CFM_SPACING|CFM_STYLE|CFM_UNDERLINETYPE|CFM_WEIGHT;
174 175 176
  cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN;
  cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED;
  cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT;
177
  cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE;
178 179 180
  
  cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
  lstrcpyW(cf.szFaceName, lf.lfFaceName);
181 182
  /* Convert system font height from logical units to twips for cf.yHeight */
  cf.yHeight = (lf.lfHeight * 72 * 1440) / (c.dpi.cy * c.dpi.cy);
183
  if (lf.lfWeight > FW_NORMAL) cf.dwEffects |= CFE_BOLD;
184 185
  cf.wWeight = lf.lfWeight;
  if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC;
186 187
  if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE;
  cf.bUnderlineType = CFU_UNDERLINE;
188
  if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT;
189 190
  cf.bPitchAndFamily = lf.lfPitchAndFamily;
  cf.bCharSet = lf.lfCharSet;
191
  cf.lcid = GetSystemDefaultLCID();
192

193 194
  style = ME_MakeStyle(&cf);
  text->pDefaultStyle = style;
195

196 197 198 199 200 201 202 203
  if (ITextHost_TxGetCharFormat(editor->texthost, &host_cf) == S_OK)
  {
    ZeroMemory(&cf, sizeof(cf));
    cf.cbSize = sizeof(cf);
    cfany_to_cf2w(&cf, (CHARFORMAT2W *)host_cf);
    ME_SetDefaultCharFormat(editor, &cf);
  }

204
  eol_len = editor->bEmulateVersion10 ? 2 : 1;
205
  para->text = ME_MakeStringN( L"\r\n", eol_len );
206

207 208 209
  run = run_create( style, MERF_ENDPARA );
  run->nCharOfs = 0;
  run->len = eol_len;
210 211
  run->para = para;
  para->eop_run = run;
212

213 214 215 216 217 218
  ME_InsertBefore( text->pLast, para_get_di( para) );
  ME_InsertBefore( text->pLast, run_get_di( run ) );
  para->prev_para = text->pFirst;
  para->next_para = text->pLast;
  text->pFirst->member.para.next_para = para_get_di( para );
  text->pLast->member.para.prev_para = para_get_di( para );
219

220
  text->pLast->member.para.nCharOfs = editor->bEmulateVersion10 ? 2 : 1;
221

222
  wine_rb_init( &editor->marked_paras, para_mark_compare );
223
  para_mark_add( editor, para );
224
  ME_DestroyContext(&c);
225
  ITextHost_TxReleaseDC( editor->texthost, hdc );
226 227
}

228
static void para_mark_rewrap_paras( ME_TextEditor *editor, ME_Paragraph *first, const ME_Paragraph *end )
229
{
230 231 232 233 234
    while (first != end)
    {
        para_mark_rewrap( editor, first );
        first = para_next( first );
    }
235 236
}

237
void editor_mark_rewrap_all( ME_TextEditor *editor )
238
{
239
    para_mark_rewrap_paras( editor, editor_first_para( editor ), editor_end_para( editor ) );
240 241
}

242
static void table_update_flags( ME_Paragraph *para )
243
{
244 245
    para->fmt.dwMask |= PFM_TABLE | PFM_TABLEROWDELIMITER;

246
    if (para_cell( para )) para->nFlags |= MEPF_CELL;
247 248 249 250 251 252 253 254 255
    else para->nFlags &= ~MEPF_CELL;

    if (para->nFlags & MEPF_ROWEND) para->fmt.wEffects |= PFE_TABLEROWDELIMITER;
    else para->fmt.wEffects &= ~PFE_TABLEROWDELIMITER;

    if (para->nFlags & (MEPF_ROWSTART | MEPF_CELL | MEPF_ROWEND))
        para->fmt.wEffects |= PFE_TABLE;
    else
        para->fmt.wEffects &= ~PFE_TABLE;
256 257
}

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
static inline BOOL para_num_same_list( const PARAFORMAT2 *item, const PARAFORMAT2 *base )
{
    return item->wNumbering == base->wNumbering &&
        item->wNumberingStart == base->wNumberingStart &&
        item->wNumberingStyle == base->wNumberingStyle &&
        !(item->wNumberingStyle & PFNS_NEWNUMBER);
}

static int para_num_get_num( ME_Paragraph *para )
{
    ME_DisplayItem *prev;
    int num = para->fmt.wNumberingStart;

    for (prev = para->prev_para; prev->type == diParagraph;
         para = &prev->member.para, prev = prev->member.para.prev_para, num++)
    {
        if (!para_num_same_list( &prev->member.para.fmt, &para->fmt )) break;
    }
    return num;
}

static ME_String *para_num_get_str( ME_Paragraph *para, WORD num )
{
281 282
    /* max 4 Roman letters (representing '8') / decade + '(' + ')' */
    ME_String *str = ME_MakeStringEmpty( 20 + 2 );
283
    WCHAR *p;
284
    static const WORD letter_base[] = { 1, 26, 26 * 26, 26 * 26 * 26 };
285 286 287 288 289 290 291 292 293 294 295 296 297
    /* roman_base should start on a '5' not a '1', otherwise the 'total' code will need adjusting.
       'N' and 'O' are what MS uses for 5000 and 10000, their version doesn't work well above 30000,
       but we'll use 'P' as the obvious extension, this gets us up to 2^16, which is all we care about. */
    static const struct
    {
        int base;
        char letter;
    }
    roman_base[] =
    {
        {50000, 'P'}, {10000, 'O'}, {5000, 'N'}, {1000, 'M'},
        {500, 'D'}, {100, 'C'}, {50, 'L'}, {10, 'X'}, {5, 'V'}, {1, 'I'}
    };
298 299
    int i, len;
    WORD letter, total, char_offset = 0;
300 301 302

    if (!str) return NULL;

303 304
    p = str->szData;

305 306 307
    if ((para->fmt.wNumberingStyle & 0xf00) == PFNS_PARENS)
        *p++ = '(';

308 309 310 311
    switch (para->fmt.wNumbering)
    {
    case PFN_ARABIC:
    default:
312
        p += swprintf( p, 20, L"%d", num );
313 314 315 316 317 318 319 320 321 322
        break;

    case PFN_LCLETTER:
        char_offset = 'a' - 'A';
        /* fall through */
    case PFN_UCLETTER:
        if (!num) num = 1;

        /* This is not base-26 (or 27) as zeros don't count unless they are leading zeros.
           It's simplest to start with the least significant letter, so first calculate how many letters are needed. */
323
        for (i = 0, total = 0; i < ARRAY_SIZE( letter_base ); i++)
324 325 326 327 328 329 330 331 332 333 334 335 336 337
        {
            total += letter_base[i];
            if (num < total) break;
        }
        len = i;
        for (i = 0; i < len; i++)
        {
            num -= letter_base[i];
            letter = (num / letter_base[i]) % 26;
            p[len - i - 1] = letter + 'A' + char_offset;
        }
        p += len;
        *p = 0;
        break;
338 339 340 341 342 343 344

    case PFN_LCROMAN:
        char_offset = 'a' - 'A';
        /* fall through */
    case PFN_UCROMAN:
        if (!num) num = 1;

345
        for (i = 0; i < ARRAY_SIZE( roman_base ); i++)
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
        {
            if (i > 0)
            {
                if (i % 2 == 0) /* eg 5000, check for 9000 */
                    total = roman_base[i].base + 4 * roman_base[i + 1].base;
                else  /* eg 1000, check for 4000 */
                    total = 4 * roman_base[i].base;

                if (num / total)
                {
                    *p++ = roman_base[(i & ~1) + 1].letter + char_offset;
                    *p++ = roman_base[i - 1].letter + char_offset;
                    num -= total;
                    continue;
                }
            }

            len = num / roman_base[i].base;
            while (len--)
            {
                *p++ = roman_base[i].letter + char_offset;
                num -= roman_base[i].base;
            }
        }
        *p = 0;
        break;
372
    }
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391

    switch (para->fmt.wNumberingStyle & 0xf00)
    {
    case PFNS_PARENS:
    case PFNS_PAREN:
        *p++ = ')';
        *p = 0;
        break;

    case PFNS_PERIOD:
        *p++ = '.';
        *p = 0;
        break;
    }

    str->nLen = p - str->szData;
    return str;
}

392 393 394 395 396 397
void para_num_init( ME_Context *c, ME_Paragraph *para )
{
    ME_Style *style;
    CHARFORMAT2W cf;
    SIZE sz;

398
    if (!para->fmt.wNumbering) return;
399 400 401 402 403 404
    if (para->para_num.style && para->para_num.text) return;

    if (!para->para_num.style)
    {
        style = para->eop_run->style;

405 406 407 408
        if (para->fmt.wNumbering == PFN_BULLET)
        {
            cf.cbSize = sizeof(cf);
            cf.dwMask = CFM_FACE | CFM_CHARSET;
409
            lstrcpyW( cf.szFaceName, L"Symbol" );
410 411 412 413 414 415 416
            cf.bCharSet = SYMBOL_CHARSET;
            style = ME_ApplyStyle( c->editor, style, &cf );
        }
        else
        {
            ME_AddRefStyle( style );
        }
417 418 419 420 421 422

        para->para_num.style = style;
    }

    if (!para->para_num.text)
    {
423 424 425
        if (para->fmt.wNumbering != PFN_BULLET)
            para->para_num.text = para_num_get_str( para, para_num_get_num( para ) );
        else
426
            para->para_num.text = ME_MakeStringConst( L"\x00b7", 1 );
427 428
    }

429
    select_style( c, para->para_num.style );
430 431
    GetTextExtentPointW( c->hDC, para->para_num.text->szData, para->para_num.text->nLen, &sz );
    para->para_num.width = sz.cx;
432
    GetTextExtentPointW( c->hDC, L" ", 1, &sz );
433 434 435 436 437 438 439 440 441 442 443 444 445 446
    para->para_num.width += sz.cx;
}

void para_num_clear( struct para_num *pn )
{
    if (pn->style)
    {
        ME_ReleaseStyle( pn->style );
        pn->style = NULL;
    }
    ME_DestroyString( pn->text );
    pn->text = NULL;
}

447
static void para_num_clear_list( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *orig_fmt )
448 449 450
{
    do
    {
451
        para_mark_rewrap( editor, para );
452 453 454 455 456 457
        para_num_clear( &para->para_num );
        if (para->next_para->type != diParagraph) break;
        para = &para->next_para->member.para;
    } while (para_num_same_list( &para->fmt, orig_fmt ));
}

458
static BOOL para_set_fmt( ME_TextEditor *editor, ME_Paragraph *para, const PARAFORMAT2 *pFmt )
459 460
{
  PARAFORMAT2 copy;
461 462
  DWORD dwMask;

463
  assert(para->fmt.cbSize == sizeof(PARAFORMAT2));
464 465 466 467 468 469 470 471
  dwMask = pFmt->dwMask;
  if (pFmt->cbSize < sizeof(PARAFORMAT))
    return FALSE;
  else if (pFmt->cbSize < sizeof(PARAFORMAT2))
    dwMask &= PFM_ALL;
  else
    dwMask &= PFM_ALL2;

472
  add_undo_set_para_fmt( editor, para );
473

474
  copy = para->fmt;
475 476

#define COPY_FIELD(m, f) \
477
  if (dwMask & (m)) {                           \
478 479
    para->fmt.dwMask |= m;                      \
    para->fmt.f = pFmt->f;                      \
480 481 482 483
  }

  COPY_FIELD(PFM_NUMBERING, wNumbering);
  COPY_FIELD(PFM_STARTINDENT, dxStartIndent);
484
  if (dwMask & PFM_OFFSETINDENT)
485
    para->fmt.dxStartIndent += pFmt->dxStartIndent;
486 487 488
  COPY_FIELD(PFM_RIGHTINDENT, dxRightIndent);
  COPY_FIELD(PFM_OFFSET, dxOffset);
  COPY_FIELD(PFM_ALIGNMENT, wAlignment);
489
  if (dwMask & PFM_TABSTOPS)
490
  {
491 492
    para->fmt.cTabCount = pFmt->cTabCount;
    memcpy(para->fmt.rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(LONG));
493
  }
494 495 496 497

#define EFFECTS_MASK (PFM_RTLPARA|PFM_KEEP|PFM_KEEPNEXT|PFM_PAGEBREAKBEFORE| \
                      PFM_NOLINENUMBER|PFM_NOWIDOWCONTROL|PFM_DONOTHYPHEN|PFM_SIDEBYSIDE| \
                      PFM_TABLE)
498 499 500
  /* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
  if (dwMask & EFFECTS_MASK)
  {
501 502 503
    para->fmt.dwMask |= dwMask & EFFECTS_MASK;
    para->fmt.wEffects &= ~HIWORD(dwMask);
    para->fmt.wEffects |= pFmt->wEffects & HIWORD(dwMask);
504
  }
505 506
#undef EFFECTS_MASK

507 508 509 510 511 512 513 514 515 516 517 518 519
  COPY_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
  COPY_FIELD(PFM_SPACEAFTER, dySpaceAfter);
  COPY_FIELD(PFM_LINESPACING, dyLineSpacing);
  COPY_FIELD(PFM_STYLE, sStyle);
  COPY_FIELD(PFM_LINESPACING, bLineSpacingRule);
  COPY_FIELD(PFM_SHADING, wShadingWeight);
  COPY_FIELD(PFM_SHADING, wShadingStyle);
  COPY_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
  COPY_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
  COPY_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
  COPY_FIELD(PFM_BORDER, wBorderSpace);
  COPY_FIELD(PFM_BORDER, wBorderWidth);
  COPY_FIELD(PFM_BORDER, wBorders);
520

521
  para->fmt.dwMask |= dwMask;
522 523
#undef COPY_FIELD

524
  if (memcmp(&copy, &para->fmt, sizeof(PARAFORMAT2)))
525
  {
526
    para_mark_rewrap( editor, para );
527 528 529 530
    if (((dwMask & PFM_NUMBERING)      && (copy.wNumbering != para->fmt.wNumbering)) ||
        ((dwMask & PFM_NUMBERINGSTART) && (copy.wNumberingStart != para->fmt.wNumberingStart)) ||
        ((dwMask & PFM_NUMBERINGSTYLE) && (copy.wNumberingStyle != para->fmt.wNumberingStyle)))
    {
531
        para_num_clear_list( editor, para, &copy );
532 533
    }
  }
534 535 536 537

  return TRUE;
}

538
/* split paragraph at the beginning of the run */
539 540
ME_Paragraph *para_split( ME_TextEditor *editor, ME_Run *run, ME_Style *style,
                          const WCHAR *eol_str, int eol_len, int paraFlags )
541
{
542 543
  ME_Paragraph *new_para = para_create( editor ), *old_para, *next_para;
  ME_Run *end_run, *next_run;
544
  int ofs, i;
545
  int run_flags = MERF_ENDPARA;
546

547 548
  if (!editor->bEmulateVersion10) /* v4.1 */
  {
549
    /* At most 1 of MEPF_CELL, MEPF_ROWSTART, or MEPF_ROWEND should be set. */
550 551
    assert( !(paraFlags & ~(MEPF_CELL | MEPF_ROWSTART | MEPF_ROWEND)) );
    assert( !(paraFlags & (paraFlags-1)) );
552
    if (paraFlags == MEPF_CELL)
553
        run_flags |= MERF_ENDCELL;
554
    else if (paraFlags == MEPF_ROWSTART)
555
      run_flags |= MERF_TABLESTART | MERF_HIDDEN;
556
  }
557 558 559 560
  else /* v1.0 - v3.0 */
    assert( !(paraFlags & (MEPF_CELL |MEPF_ROWSTART | MEPF_ROWEND)) );

  old_para = run->para;
561
  assert( old_para->fmt.cbSize == sizeof(PARAFORMAT2) );
562

563
  /* Clear any cached para numbering following this paragraph */
564 565
  if (old_para->fmt.wNumbering)
      para_num_clear_list( editor, old_para, &old_para->fmt );
566

567
  new_para->text = ME_VSplitString( old_para->text, run->nCharOfs );
568

569
  end_run = run_create( style, run_flags );
570
  ofs = end_run->nCharOfs = run->nCharOfs;
571
  end_run->len = eol_len;
572
  end_run->para = run->para;
573 574
  ME_AppendString( old_para->text, eol_str, eol_len );
  next_para = &old_para->next_para->member.para;
575

576
  add_undo_join_paras( editor, old_para->nCharOfs + ofs );
577 578

  /* Update selection cursors to point to the correct paragraph. */
579 580
  for (i = 0; i < editor->nCursors; i++)
  {
581 582
    if (editor->pCursors[i].para == old_para &&
        run->nCharOfs <= editor->pCursors[i].run->nCharOfs)
583
    {
584
      editor->pCursors[i].para = new_para;
585 586 587
    }
  }

588
  /* the new paragraph will have a different starting offset, so update its runs */
589
  for (next_run = run; next_run; next_run = run_next( next_run ))
590 591 592
  {
    next_run->nCharOfs -= ofs;
    next_run->para = new_para;
593
  }
594 595

  new_para->nCharOfs = old_para->nCharOfs + ofs;
596 597 598
  new_para->nCharOfs += eol_len;
  new_para->nFlags = 0;
  para_mark_rewrap( editor, new_para );
599

600
  /* FIXME initialize format style and call ME_SetParaFormat blah blah */
601 602
  new_para->fmt = old_para->fmt;
  new_para->border = old_para->border;
603

604
  /* insert paragraph into paragraph double linked list */
605 606 607 608
  new_para->prev_para = para_get_di( old_para );
  new_para->next_para = para_get_di( next_para );
  old_para->next_para = para_get_di( new_para );
  next_para->prev_para = para_get_di( new_para );
609 610

  /* insert end run of the old paragraph, and new paragraph, into DI double linked list */
611
  ME_InsertBefore( run_get_di( run ), para_get_di( new_para ) );
612
  ME_InsertBefore( para_get_di( new_para ), run_get_di( end_run ) );
613

614
  /* Fix up the paras' eop_run ptrs */
615 616
  new_para->eop_run = old_para->eop_run;
  old_para->eop_run = end_run;
617

618 619 620
  if (!editor->bEmulateVersion10) /* v4.1 */
  {
    if (paraFlags & (MEPF_ROWSTART | MEPF_CELL))
621
    {
622 623
      ME_Cell *cell = cell_create();
      ME_InsertBefore( para_get_di( new_para ), cell_get_di( cell ) );
624
      new_para->cell = cell;
625
      cell->next_cell = NULL;
626 627
      if (paraFlags & MEPF_ROWSTART)
      {
628
        old_para->nFlags |= MEPF_ROWSTART;
629
        cell->prev_cell = NULL;
630
        cell->parent_cell = old_para->cell;
631 632
        if (para_cell( old_para ))
          cell->nNestingLevel = para_cell( old_para )->nNestingLevel + 1;
633
        else
634
          cell->nNestingLevel = 1;
635 636 637
      }
      else
      {
638 639
        cell->prev_cell = old_para->cell;
        cell_prev( cell )->next_cell = cell;
640 641
        assert( old_para->nFlags & MEPF_CELL );
        assert( !(old_para->nFlags & MEPF_ROWSTART) );
642 643
        cell->nNestingLevel = cell_prev( cell )->nNestingLevel;
        cell->parent_cell = cell_prev( cell )->parent_cell;
644
      }
645 646 647
    }
    else if (paraFlags & MEPF_ROWEND)
    {
648
      old_para->nFlags |= MEPF_ROWEND;
649 650
      old_para->cell = old_para->cell->parent_cell;
      new_para->cell = old_para->cell;
651 652
      assert( para_prev( old_para )->nFlags & MEPF_CELL );
      assert( !(para_prev( old_para )->nFlags & MEPF_ROWSTART) );
653 654 655
      if (new_para->cell != para_next( new_para )->cell
          && para_next( new_para )->cell
          && !cell_prev( para_next( new_para )->cell ))
656 657
      {
        /* Row starts just after the row that was ended. */
658
        new_para->nFlags |= MEPF_ROWSTART;
659
      }
660
    }
661
    else new_para->cell = old_para->cell;
662

663
    table_update_flags( old_para );
664
    table_update_flags( new_para );
665 666
  }

667
  /* force rewrap of the */
668 669
  if (old_para->prev_para->type == diParagraph)
    para_mark_rewrap( editor, &old_para->prev_para->member.para );
670

671
  para_mark_rewrap( editor, &new_para->prev_para->member.para );
672

673
  /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */
674
  editor_propagate_char_ofs( next_para, NULL, eol_len );
675
  editor->nParagraphs++;
676

677
  return new_para;
678 679
}

680 681 682
/* join para with the next para keeping para's style using the paragraph fmt
   specified in use_first_fmt */
ME_Paragraph *para_join( ME_TextEditor *editor, ME_Paragraph *para, BOOL use_first_fmt )
683
{
684 685
  ME_Paragraph *next = para_next( para );
  ME_Run *end_run, *next_first_run, *tmp_run;
686
  ME_Cell *cell = NULL;
687
  int i, end_len;
688 689
  CHARFORMAT2W fmt;
  ME_Cursor startCur, endCur;
690
  ME_String *eol_str;
691

692
  assert( next && para_next( next ) );
693

694
  /* Clear any cached para numbering following this paragraph */
695
  if (para->fmt.wNumbering) para_num_clear_list( editor, para, &para->fmt );
696

697 698
  end_run = para_end_run( para );
  next_first_run = para_first_run( next );
699

700 701 702
  end_len = end_run->len;
  eol_str = ME_VSplitString( para->text, end_run->nCharOfs );
  ME_AppendString( para->text, next->text->szData, next->text->nLen );
703

704 705
  /* null char format operation to store the original char format for the ENDPARA run */
  ME_InitCharFormat2W(&fmt);
706 707 708 709
  startCur.para = para;
  startCur.run = end_run;
  endCur.para = next;
  endCur.run = next_first_run;
710 711
  startCur.nOffset = endCur.nOffset = 0;

712 713
  ME_SetCharFormat(editor, &startCur, &endCur, &fmt);

714 715
  if (!editor->bEmulateVersion10) /* v4.1 */
  {
716 717 718 719
    /* Remove cell boundary if it is between the end paragraph run and the next
     * paragraph display item. */
    if (para->cell != next->cell) cell = next->cell;

720
    /* Table cell/row properties are always moved over from the removed para. */
721
    para->nFlags = next->nFlags;
722
    para->cell = next->cell;
723
  }
724

725
  add_undo_split_para( editor, next, eol_str, cell );
726

727
  if (cell)
728
  {
729
    ME_Remove( cell_get_di( cell ) );
730 731
    if (cell_prev( cell )) cell_prev( cell )->next_cell = cell_next( cell );
    if (cell_next( cell )) cell_next( cell )->prev_cell = cell_prev( cell );
732
    ME_DestroyDisplayItem( cell_get_di( cell ) );
733 734
  }

735
  if (!use_first_fmt)
736
  {
737 738 739
    add_undo_set_para_fmt( editor, para );
    para->fmt = next->fmt;
    para->border = next->border;
740 741
  }

742 743
  /* Update selection cursors so they don't point to the removed end
   * paragraph run, and point to the correct paragraph. */
744 745
  for (i = 0; i < editor->nCursors; i++)
  {
746
    if (editor->pCursors[i].run == end_run)
747
    {
748
      editor->pCursors[i].run = next_first_run;
749 750
      editor->pCursors[i].nOffset = 0;
    }
751 752
    else if (editor->pCursors[i].para == next)
      editor->pCursors[i].para = para;
753 754
  }

755 756
  for (tmp_run = next_first_run; tmp_run; tmp_run = run_next( tmp_run ))
  {
757
    tmp_run->nCharOfs += next->nCharOfs - para->nCharOfs - end_len;
758 759
    tmp_run->para = para;
  }
760

761
  /* Fix up the para's eop_run ptr */
762
  para->eop_run = next->eop_run;
763

764 765
  ME_Remove( run_get_di( end_run ) );
  ME_DestroyDisplayItem( run_get_di( end_run) );
766

767 768 769 770
  if (editor->last_sel_start_para == next)
    editor->last_sel_start_para = para;
  if (editor->last_sel_end_para == next)
    editor->last_sel_end_para = para;
771

772 773 774
  para->next_para = next->next_para;
  next->next_para->member.para.prev_para = para_get_di( para );
  ME_Remove( para_get_di(next) );
775
  para_destroy( editor, next );
776

777
  editor_propagate_char_ofs( para_next( para ), NULL, -end_len );
778

779
  ME_CheckCharOffsets(editor);
780

781
  editor->nParagraphs--;
782 783
  para_mark_rewrap( editor, para );
  return para;
784 785
}

786
void ME_DumpParaStyleToBuf(const PARAFORMAT2 *pFmt, char buf[2048])
787 788 789 790
{
  char *p;
  p = buf;

791 792 793 794 795 796
#define DUMP(mask, name, fmt, field) \
  if (pFmt->dwMask & (mask)) p += sprintf(p, "%-22s" fmt "\n", name, pFmt->field); \
  else p += sprintf(p, "%-22sN/A\n", name);

/* we take for granted that PFE_xxx is the hiword of the corresponding PFM_xxx */
#define DUMP_EFFECT(mask, name) \
797
  p += sprintf(p, "%-22s%s\n", name, (pFmt->dwMask & (mask)) ? ((pFmt->wEffects & ((mask) >> 16)) ? "yes" : "no") : "N/A");
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835

  DUMP(PFM_NUMBERING,      "Numbering:",         "%u", wNumbering);
  DUMP_EFFECT(PFM_DONOTHYPHEN,     "Disable auto-hyphen:");
  DUMP_EFFECT(PFM_KEEP,            "No page break in para:");
  DUMP_EFFECT(PFM_KEEPNEXT,        "No page break in para & next:");
  DUMP_EFFECT(PFM_NOLINENUMBER,    "No line number:");
  DUMP_EFFECT(PFM_NOWIDOWCONTROL,  "No widow & orphan:");
  DUMP_EFFECT(PFM_PAGEBREAKBEFORE, "Page break before:");
  DUMP_EFFECT(PFM_RTLPARA,         "RTL para:");
  DUMP_EFFECT(PFM_SIDEBYSIDE,      "Side by side:");
  DUMP_EFFECT(PFM_TABLE,           "Table:");
  DUMP(PFM_OFFSETINDENT,   "Offset indent:",     "%d", dxStartIndent);
  DUMP(PFM_STARTINDENT,    "Start indent:",      "%d", dxStartIndent);
  DUMP(PFM_RIGHTINDENT,    "Right indent:",      "%d", dxRightIndent);
  DUMP(PFM_OFFSET,         "Offset:",            "%d", dxOffset);
  if (pFmt->dwMask & PFM_ALIGNMENT) {
    switch (pFmt->wAlignment) {
    case PFA_LEFT   : p += sprintf(p, "Alignment:            left\n"); break;
    case PFA_RIGHT  : p += sprintf(p, "Alignment:            right\n"); break;
    case PFA_CENTER : p += sprintf(p, "Alignment:            center\n"); break;
    case PFA_JUSTIFY: p += sprintf(p, "Alignment:            justify\n"); break;
    default         : p += sprintf(p, "Alignment:            incorrect %d\n", pFmt->wAlignment); break;
    }
  }
  else p += sprintf(p, "Alignment:            N/A\n");
  DUMP(PFM_TABSTOPS,       "Tab Stops:",         "%d", cTabCount);
  if (pFmt->dwMask & PFM_TABSTOPS) {
    int i;
    p += sprintf(p, "\t");
    for (i = 0; i < pFmt->cTabCount; i++) p += sprintf(p, "%x ", pFmt->rgxTabs[i]);
    p += sprintf(p, "\n");
  }
  DUMP(PFM_SPACEBEFORE,    "Space Before:",      "%d", dySpaceBefore);
  DUMP(PFM_SPACEAFTER,     "Space After:",       "%d", dySpaceAfter);
  DUMP(PFM_LINESPACING,    "Line spacing:",      "%d", dyLineSpacing);
  DUMP(PFM_STYLE,          "Text style:",        "%d", sStyle);
  DUMP(PFM_LINESPACING,    "Line spacing rule:", "%u", bLineSpacingRule);
  /* bOutlineLevel should be 0 */
836
  DUMP(PFM_SHADING,        "Shading Weight:",    "%u", wShadingWeight);
837 838 839 840 841 842 843 844 845 846
  DUMP(PFM_SHADING,        "Shading Style:",     "%u", wShadingStyle);
  DUMP(PFM_NUMBERINGSTART, "Numbering Start:",   "%u", wNumberingStart);
  DUMP(PFM_NUMBERINGSTYLE, "Numbering Style:",   "0x%x", wNumberingStyle);
  DUMP(PFM_NUMBERINGTAB,   "Numbering Tab:",     "%u", wNumberingStyle);
  DUMP(PFM_BORDER,         "Border Space:",      "%u", wBorderSpace);
  DUMP(PFM_BORDER,         "Border Width:",      "%u", wBorderWidth);
  DUMP(PFM_BORDER,         "Borders:",           "%u", wBorders);

#undef DUMP
#undef DUMP_EFFECT
847 848
}

849
void editor_get_selection_paras( ME_TextEditor *editor, ME_Paragraph **para, ME_Paragraph **para_end )
850
{
851
  ME_Cursor *pEndCursor = &editor->pCursors[1];
852

853 854
  *para = editor->pCursors[0].para;
  *para_end = editor->pCursors[1].para;
855 856 857
  if (*para == *para_end)
    return;

858 859 860
  if ((*para_end)->nCharOfs < (*para)->nCharOfs)
  {
    ME_Paragraph *tmp = *para;
861 862 863 864 865

    *para = *para_end;
    *para_end = tmp;
    pEndCursor = &editor->pCursors[0];
  }
866 867 868

  /* The paragraph at the end of a non-empty selection isn't included
   * if the selection ends at the start of the paragraph. */
869
  if (!pEndCursor->run->nCharOfs && !pEndCursor->nOffset)
870
    *para_end = para_prev( *para_end );
871 872 873
}


874
BOOL editor_set_selection_para_fmt( ME_TextEditor *editor, const PARAFORMAT2 *fmt )
875
{
876
    ME_Paragraph *para, *para_end;
877

878
    editor_get_selection_paras( editor, &para, &para_end );
879

880 881 882 883 884 885
    do
    {
        para_set_fmt( editor, para, fmt );
        if (para == para_end) break;
        para = para_next( para );
    } while(1);
886

887
    return TRUE;
888 889
}

890
static void para_copy_fmt( const ME_Paragraph *para, PARAFORMAT2 *fmt )
891
{
892 893 894 895 896 897 898 899 900 901
    UINT size = fmt->cbSize;

    if (fmt->cbSize >= sizeof(PARAFORMAT2))
        *fmt = para->fmt;
    else
    {
        memcpy( fmt, &para->fmt, fmt->cbSize );
        fmt->dwMask &= PFM_ALL;
    }
    fmt->cbSize = size;
902 903
}

904
void editor_get_selection_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *fmt )
905
{
906
  ME_Paragraph *para, *para_end;
907

908
  if (fmt->cbSize < sizeof(PARAFORMAT))
909
  {
910
    fmt->dwMask = 0;
911 912 913
    return;
  }

914
  editor_get_selection_paras( editor, &para, &para_end );
915

916
  para_copy_fmt( para, fmt );
917

918
  /* Invalidate values that change across the selected paragraphs. */
919 920
  while (para != para_end)
  {
921
    para = para_next( para );
922 923

#define CHECK_FIELD(m, f) \
924
    if (fmt->f != para->fmt.f) fmt->dwMask &= ~(m);
925 926 927 928 929 930

    CHECK_FIELD(PFM_NUMBERING, wNumbering);
    CHECK_FIELD(PFM_STARTINDENT, dxStartIndent);
    CHECK_FIELD(PFM_RIGHTINDENT, dxRightIndent);
    CHECK_FIELD(PFM_OFFSET, dxOffset);
    CHECK_FIELD(PFM_ALIGNMENT, wAlignment);
931
    if (fmt->dwMask & PFM_TABSTOPS)
932
    {
933 934 935
      if (fmt->cTabCount != para->fmt.cTabCount ||
          memcmp(fmt->rgxTabs, para->fmt.rgxTabs, para->fmt.cTabCount * sizeof(int) ))
        fmt->dwMask &= ~PFM_TABSTOPS;
936
    }
937

938
    if (fmt->cbSize >= sizeof(PARAFORMAT2))
939
    {
940
      fmt->dwMask &= ~((fmt->wEffects ^ para->fmt.wEffects) << 16);
941 942 943 944 945 946 947 948 949 950 951 952 953 954
      CHECK_FIELD(PFM_SPACEBEFORE, dySpaceBefore);
      CHECK_FIELD(PFM_SPACEAFTER, dySpaceAfter);
      CHECK_FIELD(PFM_LINESPACING, dyLineSpacing);
      CHECK_FIELD(PFM_STYLE, sStyle);
      CHECK_FIELD(PFM_SPACEAFTER, bLineSpacingRule);
      CHECK_FIELD(PFM_SHADING, wShadingWeight);
      CHECK_FIELD(PFM_SHADING, wShadingStyle);
      CHECK_FIELD(PFM_NUMBERINGSTART, wNumberingStart);
      CHECK_FIELD(PFM_NUMBERINGSTYLE, wNumberingStyle);
      CHECK_FIELD(PFM_NUMBERINGTAB, wNumberingTab);
      CHECK_FIELD(PFM_BORDER, wBorderSpace);
      CHECK_FIELD(PFM_BORDER, wBorderWidth);
      CHECK_FIELD(PFM_BORDER, wBorders);
    }
955
#undef CHECK_FIELD
956
  }
957
}
958

959
void editor_set_default_para_fmt( ME_TextEditor *editor, PARAFORMAT2 *pFmt )
960
{
961 962 963
    const PARAFORMAT2 *host_fmt;
    HRESULT hr;

964 965 966
    ZeroMemory(pFmt, sizeof(PARAFORMAT2));
    pFmt->cbSize = sizeof(PARAFORMAT2);
    pFmt->dwMask = PFM_ALL2;
967
    pFmt->wAlignment = PFA_LEFT;
968 969
    pFmt->sStyle = -1;
    pFmt->bOutlineLevel = TRUE;
970 971 972 973 974 975 976 977 978

    hr = ITextHost_TxGetParaFormat( editor->texthost, (const PARAFORMAT **)&host_fmt );
    if (SUCCEEDED(hr))
    {
        /* Just use the alignment for now */
        if (host_fmt->dwMask & PFM_ALIGNMENT)
            pFmt->wAlignment = host_fmt->wAlignment;
        ITextHost_OnTxParaFormatChange( editor->texthost, (PARAFORMAT *)pFmt );
    }
979
}