1 /*
2 * RichEdit - functions dealing with editor object
3 *
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2005 by Cihan Altinay
6 * Copyright 2005 by Phil Krylov
7 * Copyright 2008 Eric Pouech
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 /*
25 API implementation status:
26
27 Messages (ANSI versions not done yet)
28 + EM_AUTOURLDETECT 2.0
29 + EM_CANPASTE
30 + EM_CANREDO 2.0
31 + EM_CANUNDO
32 + EM_CHARFROMPOS
33 - EM_DISPLAYBAND
34 + EM_EMPTYUNDOBUFFER
35 + EM_EXGETSEL
36 + EM_EXLIMITTEXT
37 + EM_EXLINEFROMCHAR
38 + EM_EXSETSEL
39 + EM_FINDTEXT (only FR_DOWN flag implemented)
40 + EM_FINDTEXTEX (only FR_DOWN flag implemented)
41 - EM_FINDWORDBREAK
42 - EM_FMTLINES
43 - EM_FORMATRANGE
44 + EM_GETAUTOURLDETECT 2.0
45 - EM_GETBIDIOPTIONS 3.0
46 - EM_GETCHARFORMAT (partly done)
47 - EM_GETEDITSTYLE
48 + EM_GETEVENTMASK
49 + EM_GETFIRSTVISIBLELINE (can be optimized if needed)
50 - EM_GETIMECOLOR 1.0asian
51 - EM_GETIMECOMPMODE 2.0
52 - EM_GETIMEOPTIONS 1.0asian
53 - EM_GETIMESTATUS
54 - EM_GETLANGOPTIONS 2.0
55 + EM_GETLIMITTEXT
56 + EM_GETLINE
57 + EM_GETLINECOUNT returns number of rows, not of paragraphs
58 + EM_GETMODIFY
59 - EM_GETOLEINTERFACE
60 + EM_GETOPTIONS
61 + EM_GETPARAFORMAT
62 + EM_GETPASSWORDCHAR 2.0
63 - EM_GETPUNCTUATION 1.0asian
64 + EM_GETRECT
65 - EM_GETREDONAME 2.0
66 + EM_GETSEL
67 + EM_GETSELTEXT (ANSI&Unicode)
68 + EM_GETSCROLLPOS 3.0 (only Y value valid)
69 ! - EM_GETTHUMB
70 + EM_GETTEXTEX 2.0
71 + EM_GETTEXTLENGTHEX (GTL_PRECISE unimplemented)
72 - EM_GETTEXTMODE 2.0
73 ? + EM_GETTEXTRANGE (ANSI&Unicode)
74 - EM_GETTYPOGRAPHYOPTIONS 3.0
75 - EM_GETUNDONAME
76 + EM_GETWORDBREAKPROC
77 - EM_GETWORDBREAKPROCEX
78 - EM_GETWORDWRAPMODE 1.0asian
79 + EM_GETZOOM 3.0
80 + EM_HIDESELECTION
81 + EM_LIMITTEXT (Also called EM_SETLIMITTEXT)
82 + EM_LINEFROMCHAR
83 + EM_LINEINDEX
84 + EM_LINELENGTH
85 + EM_LINESCROLL
86 - EM_PASTESPECIAL
87 + EM_POSFROMCHAR
88 + EM_REDO 2.0
89 + EM_REQUESTRESIZE
90 + EM_REPLACESEL (proper style?) ANSI&Unicode
91 + EM_SCROLL
92 + EM_SCROLLCARET
93 - EM_SELECTIONTYPE
94 - EM_SETBIDIOPTIONS 3.0
95 + EM_SETBKGNDCOLOR
96 + EM_SETCHARFORMAT (partly done, no ANSI)
97 - EM_SETEDITSTYLE
98 + EM_SETEVENTMASK (few notifications supported)
99 - EM_SETFONTSIZE
100 - EM_SETIMECOLOR 1.0asian
101 - EM_SETIMEOPTIONS 1.0asian
102 - EM_SETLANGOPTIONS 2.0
103 - EM_SETLIMITTEXT
104 + EM_SETMODIFY (not sure if implementation is correct)
105 - EM_SETOLECALLBACK
106 + EM_SETOPTIONS (partially implemented)
107 - EM_SETPALETTE 2.0
108 + EM_SETPARAFORMAT
109 + EM_SETPASSWORDCHAR 2.0
110 - EM_SETPUNCTUATION 1.0asian
111 + EM_SETREADONLY no beep on modification attempt
112 + EM_SETRECT
113 + EM_SETRECTNP (EM_SETRECT without repainting)
114 + EM_SETSEL
115 + EM_SETSCROLLPOS 3.0
116 - EM_SETTABSTOPS 3.0
117 - EM_SETTARGETDEVICE (partial)
118 + EM_SETTEXTEX 3.0 (proper style?)
119 - EM_SETTEXTMODE 2.0
120 - EM_SETTYPOGRAPHYOPTIONS 3.0
121 + EM_SETUNDOLIMIT 2.0
122 + EM_SETWORDBREAKPROC (used only for word movement at the moment)
123 - EM_SETWORDBREAKPROCEX
124 - EM_SETWORDWRAPMODE 1.0asian
125 + EM_SETZOOM 3.0
126 + EM_SHOWSCROLLBAR 2.0
127 + EM_STOPGROUPTYPING 2.0
128 + EM_STREAMIN
129 + EM_STREAMOUT
130 + EM_UNDO
131 + WM_CHAR
132 + WM_CLEAR
133 + WM_COPY
134 + WM_CUT
135 + WM_GETDLGCODE (the current implementation is incomplete)
136 + WM_GETTEXT (ANSI&Unicode)
137 + WM_GETTEXTLENGTH (ANSI version sucks)
138 + WM_PASTE
139 + WM_SETFONT
140 + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
141 - WM_STYLECHANGING
142 - WM_STYLECHANGED (things like read-only flag)
143 + WM_UNICHAR
144
145 Notifications
146
147 * EN_CHANGE (sent from the wrong place)
148 - EN_CORRECTTEXT
149 - EN_DROPFILES
150 - EN_ERRSPACE
151 - EN_HSCROLL
152 - EN_IMECHANGE
153 + EN_KILLFOCUS
154 - EN_LINK
155 - EN_MAXTEXT
156 - EN_MSGFILTER
157 - EN_OLEOPFAILED
158 - EN_PROTECTED
159 + EN_REQUESTRESIZE
160 - EN_SAVECLIPBOARD
161 + EN_SELCHANGE
162 + EN_SETFOCUS
163 - EN_STOPNOUNDO
164 * EN_UPDATE (sent from the wrong place)
165 - EN_VSCROLL
166
167 Styles
168
169 - ES_AUTOHSCROLL
170 - ES_AUTOVSCROLL
171 - ES_CENTER
172 + ES_DISABLENOSCROLL (scrollbar is always visible)
173 - ES_EX_NOCALLOLEINIT
174 - ES_LEFT
175 - ES_MULTILINE (currently single line controls aren't supported)
176 - ES_NOIME
177 - ES_READONLY (I'm not sure if beeping is the proper behaviour)
178 - ES_RIGHT
179 - ES_SAVESEL
180 - ES_SELFIME
181 - ES_SUNKEN
182 - ES_VERTICAL
183 - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part)
184 - WS_SETFONT
185 - WS_HSCROLL
186 + WS_VSCROLL
187 */
188
189 /*
190 * RICHED20 TODO (incomplete):
191 *
192 * - messages/styles/notifications listed above
193 * - add remaining CHARFORMAT/PARAFORMAT fields
194 * - right/center align should strip spaces from the beginning
195 * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
196 * - COM interface (looks like a major pain in the TODO list)
197 * - calculate heights of pictures (half-done)
198 * - horizontal scrolling (not even started)
199 * - hysteresis during wrapping (related to scrollbars appearing/disappearing)
200 * - find/replace
201 * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
202 * - italic caret with italic fonts
203 * - IME
204 * - most notifications aren't sent at all (the most important ones are)
205 * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
206 * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
207 * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
208 * - full justification
209 * - hyphenation
210 * - tables
211 * - ListBox & ComboBox not implemented
212 *
213 * Bugs that are probably fixed, but not so easy to verify:
214 * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
215 * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does)
216 * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical)
217 * - caret shouldn't be displayed when selection isn't empty
218 * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious)
219 * - undo for setting default format (done, might be buggy)
220 * - styles might be not released properly (looks like they work like charm, but who knows?
221 *
222 */
223
224 #include "editor.h"
225 #include "commdlg.h"
226 #include "winreg.h"
227 #define NO_SHLWAPI_STREAM
228 #include "shlwapi.h"
229 #include "rtf.h"
230 #include "imm.h"
231 #include "res.h"
232
233 #define STACK_SIZE_DEFAULT 100
234 #define STACK_SIZE_MAX 1000
235
236 #define TEXT_LIMIT_DEFAULT 32767
237
238 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
239
240 static BOOL ME_RegisterEditorClass(HINSTANCE);
241
242 static const WCHAR RichEdit20W[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '', 'W', 0};
243 static const WCHAR RichEdit50W[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '', 'W', 0};
244 static const WCHAR REListBox20W[] = {'R','E','L','i','s','t','B','o','x','2','','W', 0};
245 static const WCHAR REComboBox20W[] = {'R','E','C','o','m','b','o','B','o','x','2','','W', 0};
246 static HCURSOR hLeft;
247
248 int me_debug = 0;
249 HANDLE me_heap = NULL;
250
251 static BOOL ME_ListBoxRegistered = FALSE;
252 static BOOL ME_ComboBoxRegistered = FALSE;
253
254 static inline int is_version_nt(void)
255 {
256 return !(GetVersion() & 0x80000000);
257 }
258
259 static ME_TextBuffer *ME_MakeText(void) {
260
261 ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer);
262
263 ME_DisplayItem *p1 = ME_MakeDI(diTextStart);
264 ME_DisplayItem *p2 = ME_MakeDI(diTextEnd);
265
266 p1->prev = NULL;
267 p1->next = p2;
268 p2->prev = p1;
269 p2->next = NULL;
270 p1->member.para.next_para = p2;
271 p2->member.para.prev_para = p1;
272 p2->member.para.nCharOfs = 0;
273
274 buf->pFirst = p1;
275 buf->pLast = p2;
276 buf->pCharStyle = NULL;
277
278 return buf;
279 }
280
281
282 static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
283 {
284 WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
285 WCHAR *pText;
286
287 TRACE("%08x %p\n", dwFormat, stream);
288
289 do {
290 long nWideChars = 0;
291
292 if (!stream->dwSize)
293 {
294 ME_StreamInFill(stream);
295 if (stream->editstream->dwError)
296 break;
297 if (!stream->dwSize)
298 break;
299 }
300
301 if (!(dwFormat & SF_UNICODE))
302 {
303 /* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
304 nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
305 pText = wszText;
306 }
307 else
308 {
309 nWideChars = stream->dwSize >> 1;
310 pText = (WCHAR *)stream->buffer;
311 }
312
313 ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
314 if (stream->dwSize == 0)
315 break;
316 stream->dwSize = 0;
317 } while(1);
318 ME_CommitUndo(editor);
319 ME_UpdateRepaint(editor);
320 return 0;
321 }
322
323 static void ME_ApplyBorderProperties(RTF_Info *info,
324 ME_BorderRect *borderRect,
325 RTFBorder *borderDef)
326 {
327 int i, colorNum;
328 ME_Border *pBorders[] = {&borderRect->top,
329 &borderRect->left,
330 &borderRect->bottom,
331 &borderRect->right};
332 for (i = 0; i < 4; i++)
333 {
334 RTFColor *colorDef = info->colorList;
335 pBorders[i]->width = borderDef[i].width;
336 colorNum = borderDef[i].color;
337 while (colorDef && colorDef->rtfCNum != colorNum)
338 colorDef = colorDef->rtfNextColor;
339 if (colorDef)
340 pBorders[i]->colorRef = RGB(
341 colorDef->rtfCRed >= 0 ? colorDef->rtfCRed : 0,
342 colorDef->rtfCGreen >= 0 ? colorDef->rtfCGreen : 0,
343 colorDef->rtfCBlue >= 0 ? colorDef->rtfCBlue : 0);
344 else
345 pBorders[i]->colorRef = RGB(0, 0, 0);
346 }
347 }
348
349 void ME_RTFCharAttrHook(RTF_Info *info)
350 {
351 CHARFORMAT2W fmt;
352 fmt.cbSize = sizeof(fmt);
353 fmt.dwMask = 0;
354 fmt.dwEffects = 0;
355
356 switch(info->rtfMinor)
357 {
358 case rtfPlain:
359 /* FIXME add more flags once they're implemented */
360 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINETYPE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
361 fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
362 fmt.yHeight = 12*20; /* 12pt */
363 fmt.wWeight = FW_NORMAL;
364 fmt.bUnderlineType = CFU_UNDERLINENONE;
365 break;
366 case rtfBold:
367 fmt.dwMask = CFM_BOLD | CFM_WEIGHT;
368 fmt.dwEffects = info->rtfParam ? CFE_BOLD : 0;
369 fmt.wWeight = info->rtfParam ? FW_BOLD : FW_NORMAL;
370 break;
371 case rtfItalic:
372 fmt.dwMask = CFM_ITALIC;
373 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
374 break;
375 case rtfUnderline:
376 fmt.dwMask = CFM_UNDERLINETYPE;
377 fmt.bUnderlineType = info->rtfParam ? CFU_CF1UNDERLINE : CFU_UNDERLINENONE;
378 break;
379 case rtfDotUnderline:
380 fmt.dwMask = CFM_UNDERLINETYPE;
381 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOTTED : CFU_UNDERLINENONE;
382 break;
383 case rtfDbUnderline:
384 fmt.dwMask = CFM_UNDERLINETYPE;
385 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEDOUBLE : CFU_UNDERLINENONE;
386 break;
387 case rtfWordUnderline:
388 fmt.dwMask = CFM_UNDERLINETYPE;
389 fmt.bUnderlineType = info->rtfParam ? CFU_UNDERLINEWORD : CFU_UNDERLINENONE;
390 break;
391 case rtfNoUnderline:
392 fmt.dwMask = CFM_UNDERLINETYPE;
393 fmt.bUnderlineType = CFU_UNDERLINENONE;
394 break;
395 case rtfStrikeThru:
396 fmt.dwMask = CFM_STRIKEOUT;
397 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
398 break;
399 case rtfSubScript:
400 case rtfSuperScript:
401 case rtfSubScrShrink:
402 case rtfSuperScrShrink:
403 case rtfNoSuperSub:
404 fmt.dwMask = CFM_SUBSCRIPT|CFM_SUPERSCRIPT;
405 if (info->rtfMinor == rtfSubScrShrink) fmt.dwEffects = CFE_SUBSCRIPT;
406 if (info->rtfMinor == rtfSuperScrShrink) fmt.dwEffects = CFE_SUPERSCRIPT;
407 if (info->rtfMinor == rtfNoSuperSub) fmt.dwEffects = 0;
408 break;
409 case rtfInvisible:
410 fmt.dwMask = CFM_HIDDEN;
411 fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
412 break;
413 case rtfBackColor:
414 fmt.dwMask = CFM_BACKCOLOR;
415 fmt.dwEffects = 0;
416 if (info->rtfParam == 0)
417 fmt.dwEffects = CFE_AUTOBACKCOLOR;
418 else if (info->rtfParam != rtfNoParam)
419 {
420 RTFColor *c = RTFGetColor(info, info->rtfParam);
421 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
422 }
423 break;
424 case rtfForeColor:
425 fmt.dwMask = CFM_COLOR;
426 fmt.dwEffects = 0;
427 if (info->rtfParam == 0)
428 fmt.dwEffects = CFE_AUTOCOLOR;
429 else if (info->rtfParam != rtfNoParam)
430 {
431 RTFColor *c = RTFGetColor(info, info->rtfParam);
432 if (c)
433 fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
434 else
435 fmt.crTextColor = 0;
436 }
437 break;
438 case rtfFontNum:
439 if (info->rtfParam != rtfNoParam)
440 {
441 RTFFont *f = RTFGetFont(info, info->rtfParam);
442 if (f)
443 {
444 MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
445 fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
446 fmt.bCharSet = f->rtfFCharSet;
447 fmt.dwMask = CFM_FACE | CFM_CHARSET;
448 fmt.bPitchAndFamily = f->rtfFPitch | (f->rtfFFamily << 4);
449 }
450 }
451 break;
452 case rtfFontSize:
453 fmt.dwMask = CFM_SIZE;
454 if (info->rtfParam != rtfNoParam)
455 fmt.yHeight = info->rtfParam*10;
456 break;
457 }
458 if (fmt.dwMask) {
459 ME_Style *style2;
460 RTFFlushOutputBuffer(info);
461 /* FIXME too slow ? how come ? */
462 style2 = ME_ApplyStyle(info->style, &fmt);
463 ME_ReleaseStyle(info->style);
464 info->style = style2;
465 info->styleChanged = TRUE;
466 }
467 }
468
469 /* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
470 the same tags mean different things in different contexts */
471 void ME_RTFParAttrHook(RTF_Info *info)
472 {
473 PARAFORMAT2 fmt;
474 fmt.cbSize = sizeof(fmt);
475 fmt.dwMask = 0;
476
477 switch(info->rtfMinor)
478 {
479 case rtfParDef: /* restores default paragraph attributes */
480 if (!info->editor->bEmulateVersion10) /* v4.1 */
481 info->borderType = RTFBorderParaLeft;
482 else /* v1.0 - 3.0 */
483 info->borderType = RTFBorderParaTop;
484 fmt.dwMask = PFM_ALIGNMENT | PFM_BORDER | PFM_LINESPACING | PFM_TABSTOPS |
485 PFM_OFFSET | PFM_RIGHTINDENT | PFM_SPACEAFTER | PFM_SPACEBEFORE |
486 PFM_STARTINDENT;
487 /* TODO: numbering, shading */
488 fmt.wAlignment = PFA_LEFT;
489 fmt.cTabCount = 0;
490 fmt.dxOffset = fmt.dxStartIndent = fmt.dxRightIndent = 0;
491 fmt.wBorderWidth = fmt.wBorders = 0;
492 fmt.wBorderSpace = 0;
493 fmt.bLineSpacingRule = 0;
494 fmt.dySpaceBefore = fmt.dySpaceAfter = 0;
495 fmt.dyLineSpacing = 0;
496 if (!info->editor->bEmulateVersion10) /* v4.1 */
497 {
498 if (info->tableDef && info->tableDef->tableRowStart &&
499 info->tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
500 {
501 ME_Cursor cursor;
502 ME_DisplayItem *para;
503 /* We are just after a table row. */
504 RTFFlushOutputBuffer(info);
505 cursor = info->editor->pCursors[0];
506 para = ME_GetParagraph(cursor.pRun);
507 if (para == info->tableDef->tableRowStart->member.para.next_para
508 && !cursor.nOffset && !cursor.pRun->member.run.nCharOfs)
509 {
510 /* Since the table row end, no text has been inserted, and the \intbl
511 * control word has not be used. We can confirm that we are not in a
512 * table anymore.
513 */
514 info->tableDef->tableRowStart = NULL;
515 info->canInheritInTbl = FALSE;
516 }
517 }
518 } else { /* v1.0 - v3.0 */
519 fmt.dwMask |= PFM_TABLE;
520 fmt.wEffects &= ~PFE_TABLE;
521 }
522 break;
523 case rtfNestLevel:
524 if (!info->editor->bEmulateVersion10) /* v4.1 */
525 {
526 while (info->rtfParam > info->nestingLevel) {
527 RTFTable *tableDef = ALLOC_OBJ(RTFTable);
528 ZeroMemory(tableDef, sizeof(RTFTable));
529 tableDef->parent = info->tableDef;
530 info->tableDef = tableDef;
531
532 RTFFlushOutputBuffer(info);
533 if (tableDef->tableRowStart &&
534 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
535 {
536 ME_DisplayItem *para = tableDef->tableRowStart;
537 para = para->member.para.next_para;
538 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
539 tableDef->tableRowStart = para;
540 } else {
541 ME_Cursor cursor;
542 WCHAR endl = '\r';
543 cursor = info->editor->pCursors[0];
544 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
545 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
546 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
547 }
548
549 info->nestingLevel++;
550 }
551 info->canInheritInTbl = FALSE;
552 }
553 break;
554 case rtfInTable:
555 {
556 if (!info->editor->bEmulateVersion10) /* v4.1 */
557 {
558 if (info->nestingLevel < 1)
559 {
560 RTFTable *tableDef;
561 if (!info->tableDef)
562 {
563 info->tableDef = ALLOC_OBJ(RTFTable);
564 ZeroMemory(info->tableDef, sizeof(RTFTable));
565 }
566 tableDef = info->tableDef;
567 RTFFlushOutputBuffer(info);
568 if (tableDef->tableRowStart &&
569 tableDef->tableRowStart->member.para.nFlags & MEPF_ROWEND)
570 {
571 ME_DisplayItem *para = tableDef->tableRowStart;
572 para = para->member.para.next_para;
573 para = ME_InsertTableRowStartAtParagraph(info->editor, para);
574 tableDef->tableRowStart = para;
575 } else {
576 ME_Cursor cursor;
577 WCHAR endl = '\r';
578 cursor = info->editor->pCursors[0];
579 if (cursor.nOffset || cursor.pRun->member.run.nCharOfs)
580 ME_InsertTextFromCursor(info->editor, 0, &endl, 1, info->style);
581 tableDef->tableRowStart = ME_InsertTableRowStartFromCursor(info->editor);
582 }
583 info->nestingLevel = 1;
584 info->canInheritInTbl = TRUE;
585 }
586 return;
587 } else { /* v1.0 - v3.0 */
588 fmt.dwMask |= PFM_TABLE;
589 fmt.wEffects |= PFE_TABLE;
590 }
591 break;
592 }
593 case rtfFirstIndent:
594 ME_GetSelectionParaFormat(info->editor, &fmt);
595 fmt.dwMask = PFM_STARTINDENT | PFM_OFFSET;
596 fmt.dxStartIndent += fmt.dxOffset + info->rtfParam;
597 fmt.dxOffset = -info->rtfParam;
598 break;
599 case rtfLeftIndent:
600 ME_GetSelectionParaFormat(info->editor, &fmt);
601 fmt.dwMask = PFM_STARTINDENT;
602 fmt.dxStartIndent = info->rtfParam - fmt.dxOffset;
603 break;
604 case rtfRightIndent:
605 fmt.dwMask = PFM_RIGHTINDENT;
606 fmt.dxRightIndent = info->rtfParam;
607 break;
608 case rtfQuadLeft:
609 case rtfQuadJust:
610 fmt.dwMask = PFM_ALIGNMENT;
611 fmt.wAlignment = PFA_LEFT;
612 break;
613 case rtfQuadRight:
614 fmt.dwMask = PFM_ALIGNMENT;
615 fmt.wAlignment = PFA_RIGHT;
616 break;
617 case rtfQuadCenter:
618 fmt.dwMask = PFM_ALIGNMENT;
619 fmt.wAlignment = PFA_CENTER;
620 break;
621 case rtfTabPos:
622 ME_GetSelectionParaFormat(info->editor, &fmt);
623 if (!(fmt.dwMask & PFM_TABSTOPS))
624 {
625 fmt.cTabCount = 0;
626 }
627 if (fmt.cTabCount < MAX_TAB_STOPS && info->rtfParam < 0x1000000)
628 fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
629 fmt.dwMask = PFM_TABSTOPS;
630 break;
631 case rtfKeep:
632 fmt.dwMask = PFM_KEEP;
633 fmt.wEffects = PFE_KEEP;
634 break;
635 case rtfNoWidowControl:
636 fmt.dwMask = PFM_NOWIDOWCONTROL;
637 fmt.wEffects = PFE_NOWIDOWCONTROL;
638 break;
639 case rtfKeepNext:
640 fmt.dwMask = PFM_KEEPNEXT;
641 fmt.wEffects = PFE_KEEPNEXT;
642 break;
643 case rtfSpaceAfter:
644 fmt.dwMask = PFM_SPACEAFTER;
645 fmt.dySpaceAfter = info->rtfParam;
646 break;
647 case rtfSpaceBefore:
648 fmt.dwMask = PFM_SPACEBEFORE;
649 fmt.dySpaceBefore = info->rtfParam;
650 break;
651 case rtfSpaceBetween:
652 fmt.dwMask = PFM_LINESPACING;
653 if ((int)info->rtfParam > 0)
654 {
655 fmt.dyLineSpacing = info->rtfParam;
656 fmt.bLineSpacingRule = 3;
657 }
658 else
659 {
660 fmt.dyLineSpacing = info->rtfParam;
661 fmt.bLineSpacingRule = 4;
662 }
663 case rtfSpaceMultiply:
664 fmt.dwMask = PFM_LINESPACING;
665 fmt.dyLineSpacing = info->rtfParam * 20;
666 fmt.bLineSpacingRule = 5;
667 break;
668 case rtfParBullet:
669 fmt.dwMask = PFM_NUMBERING;
670 fmt.wNumbering = PFN_BULLET;
671 break;
672 case rtfParSimple:
673 fmt.dwMask = PFM_NUMBERING;
674 fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
675 break;
676 case rtfParNumDecimal:
677 fmt.dwMask = PFM_NUMBERING;
678 fmt.wNumbering = 2; /* FIXME: MSDN says it's not used ?? */
679 break;
680 case rtfParNumIndent:
681 fmt.dwMask = PFM_NUMBERINGTAB;
682 fmt.wNumberingTab = info->rtfParam;
683 break;
684 case rtfParNumStartAt:
685 fmt.dwMask = PFM_NUMBERINGSTART;
686 fmt.wNumberingStart = info->rtfParam;
687 break;
688 case rtfBorderLeft:
689 info->borderType = RTFBorderParaLeft;
690 ME_GetSelectionParaFormat(info->editor, &fmt);
691 if (!(fmt.dwMask & PFM_BORDER))
692 {
693 fmt.wBorderSpace = 0;
694 fmt.wBorderWidth = 1;
695 fmt.wBorders = 0;
696 }
697 fmt.wBorders |= 1;
698 fmt.dwMask = PFM_BORDER;
699 break;
700 case rtfBorderRight:
701 info->borderType = RTFBorderParaRight;
702 ME_GetSelectionParaFormat(info->editor, &fmt);
703 if (!(fmt.dwMask & PFM_BORDER))
704 {
705 fmt.wBorderSpace = 0;
706 fmt.wBorderWidth = 1;
707 fmt.wBorders = 0;
708 }
709 fmt.wBorders |= 2;
710 fmt.dwMask = PFM_BORDER;
711 break;
712 case rtfBorderTop:
713 info->borderType = RTFBorderParaTop;
714 ME_GetSelectionParaFormat(info->editor, &fmt);
715 if (!(fmt.dwMask & PFM_BORDER))
716 {
717 fmt.wBorderSpace = 0;
718 fmt.wBorderWidth = 1;
719 fmt.wBorders = 0;
720 }
721 fmt.wBorders |= 4;
722 fmt.dwMask = PFM_BORDER;
723 break;
724 case rtfBorderBottom:
725 info->borderType = RTFBorderParaBottom;
726 ME_GetSelectionParaFormat(info->editor, &fmt);
727 if (!(fmt.dwMask & PFM_BORDER))
728 {
729 fmt.wBorderSpace = 0;
730 fmt.wBorderWidth = 1;
731 fmt.wBorders = 0;
732 }
733 fmt.wBorders |= 8;
734 fmt.dwMask = PFM_BORDER;
735 break;
736 case rtfBorderSingle:
737 ME_GetSelectionParaFormat(info->editor, &fmt);
738 /* we assume that borders have been created before (RTF spec) */
739 fmt.wBorders &= ~0x700;
740 fmt.wBorders |= 1 << 8;
741 fmt.dwMask = PFM_BORDER;
742 break;
743 case rtfBorderThick:
744 ME_GetSelectionParaFormat(info->editor, &fmt);
745 /* we assume that borders have been created before (RTF spec) */
746 fmt.wBorders &= ~0x700;
747 fmt.wBorders |= 2 << 8;
748 fmt.dwMask = PFM_BORDER;
749 break;
750 case rtfBorderShadow:
751 ME_GetSelectionParaFormat(info->editor, &fmt);
752 /* we assume that borders have been created before (RTF spec) */
753 fmt.wBorders &= ~0x700;
754 fmt.wBorders |= 10 << 8;
755 fmt.dwMask = PFM_BORDER;
756 break;
757 case rtfBorderDouble:
758 ME_GetSelectionParaFormat(info->editor, &fmt);
759 /* we assume that borders have been created before (RTF spec) */
760 fmt.wBorders &= ~0x700;
761 fmt.wBorders |= 7 << 8;
762 fmt.dwMask = PFM_BORDER;
763 break;
764 case rtfBorderDot:
765 ME_GetSelectionParaFormat(info->editor, &fmt);
766 /* we assume that borders have been created before (RTF spec) */
767 fmt.wBorders &= ~0x700;
768 fmt.wBorders |= 11 << 8;
769 fmt.dwMask = PFM_BORDER;
770 break;
771 case rtfBorderWidth:
772 {
773 int borderSide = info->borderType & RTFBorderSideMask;
774 RTFTable *tableDef = info->tableDef;
775 ME_GetSelectionParaFormat(info->editor, &fmt);
776 /* we assume that borders have been created before (RTF spec) */
777 fmt.wBorderWidth |= ((info->rtfParam / 15) & 7) << 8;
778 if ((info->borderType & RTFBorderTypeMask) == RTFBorderTypeCell)
779 {
780 RTFBorder *border;
781 if (!tableDef || tableDef->numCellsDefined >= MAX_TABLE_CELLS)
782 break;
783 border = &tableDef->cells[tableDef->numCellsDefined].border[borderSide];
784 border->width = info->rtfParam;
785 break;
786 }
787 fmt.dwMask = PFM_BORDER;
788 break;
789 }
790 case rtfBorderSpace:
791 ME_GetSelectionParaFormat(info->editor, &fmt);
792 /* we assume that borders have been created before (RTF spec) */
793 fmt.wBorderSpace = info->rtfParam;
794 fmt.dwMask = PFM_BORDER;
795 break;
796 case rtfBorderColor:
797 {
798 RTFTable *tableDef = info->tableDef;
799 int borderSide = info->borderType & RTFBorderSideMask;
800 int borderType = info->borderType & RTFBorderTypeMask;
801 switch(borderType)
802 {
803 case RTFBorderTypePara:
804 if (!info->editor->bEmulateVersion10) /* v4.1 */
805 break;
806 /* v1.0 - 3.0 treat paragraph and row borders the same. */
807 case RTFBorderTypeRow:
808 if (tableDef) {
809 tableDef->border[borderSide].color = info->rtfParam;
810 }
811 break;
812 case RTFBorderTypeCell:
813 if (tableDef && tableDef->numCellsDefined < MAX_TABLE_CELLS) {
814 tableDef->cells[tableDef->numCellsDefined].border[borderSide].color = info->rtfParam;
815 }
816 break;
817 }
818 break;
819 }
820 }
821 if (fmt.dwMask) {
822 RTFFlushOutputBuffer(info);
823 /* FIXME too slow ? how come ?*/
824 ME_SetSelectionParaFormat(info->editor, &fmt);
825 }
826 }
827
828 void ME_RTFTblAttrHook(RTF_Info *info)
829 {
830 switch (info->rtfMinor)
831 {
832 case rtfRowDef:
833 {
834 if (!info->editor->bEmulateVersion10) /* v4.1 */
835 info->borderType = 0; /* Not sure */
836 else /* v1.0 - 3.0 */
837 info->borderType = RTFBorderRowTop;
838 if (!info->tableDef) {
839 info->tableDef = ME_MakeTableDef(info->editor);
840 } else {
841 ME_InitTableDef(info->editor, info->tableDef);
842 }
843 break;
844 }
845 case rtfCellPos:
846 {
847 int cellNum;
848 if (!info->tableDef)
849 {
850 info->tableDef = ME_MakeTableDef(info->editor);
851 }
852 cellNum = info->tableDef->numCellsDefined;
853 if (cellNum >= MAX_TABLE_CELLS)
854 break;
855 info->tableDef->cells[cellNum].rightBoundary = info->rtfParam;
856 if (cellNum < MAX_TAB_STOPS) {
857 /* Tab stops were used to store cell positions before v4.1 but v4.1
858 * still seems to set the tabstops without using them. */
859 ME_DisplayItem *para = ME_GetParagraph(info->editor->pCursors[0].pRun);
860 PARAFORMAT2 *pFmt = para->member.para.pFmt;
861 pFmt->rgxTabs[cellNum] &= ~0x00FFFFFF;
862 pFmt->rgxTabs[cellNum] = 0x00FFFFFF & info->rtfParam;
863 }
864 info->tableDef->numCellsDefined++;
865 break;
866 }
867 case rtfRowBordTop:
868 info->borderType = RTFBorderRowTop;
869 break;
870 case rtfRowBordLeft:
871 info->borderType = RTFBorderRowLeft;
872 break;
873 case rtfRowBordBottom:
874 info->borderType = RTFBorderRowBottom;
875 break;
876 case rtfRowBordRight:
877 info->borderType = RTFBorderRowRight;
878 break;
879 case rtfCellBordTop:
880 info->borderType = RTFBorderCellTop;
881 break;
882 case rtfCellBordLeft:
883 info->borderType = RTFBorderCellLeft;
884 break;
885 case rtfCellBordBottom:
886 info->borderType = RTFBorderCellBottom;
887 break;
888 case rtfCellBordRight:
889 info->borderType = RTFBorderCellRight;
890 break;
891 case rtfRowGapH:
892 if (info->tableDef)
893 info->tableDef->gapH = info->rtfParam;
894 break;
895 case rtfRowLeftEdge:
896 if (info->tableDef)
897 info->tableDef->leftEdge = info->rtfParam;
898 break;
899 }
900 }
901
902 void ME_RTFSpecialCharHook(RTF_Info *info)
903 {
904 RTFTable *tableDef = info->tableDef;
905 switch (info->rtfMinor)
906 {
907 case rtfNestCell:
908 if (info->editor->bEmulateVersion10) /* v1.0 - v3.0 */
909 break;
910 /* else fall through since v4.1 treats rtfNestCell and rtfCell the same */