/****************************************************************************** * ME_GetRunSizeCommon * * Finds width, height, ascent and descent of a run, up to given character * (nLen). */ SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen, int startx, int *pAscent, int *pDescent) { SIZE size; WCHAR spaceW[] = {' ',0}; nLen = min( nLen, run->len ); if (run->nFlags & MERF_ENDPARA) { nLen = min( nLen, 1 ); ME_GetTextExtent(c, spaceW, nLen, run->style, &size); } else if (para->nFlags & MEPF_COMPLEX) { size.cx = run->nWidth; } else if (c->editor->cPasswordMask) { ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen); ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size); ME_DestroyString(szMasked); } else { ME_GetTextExtent(c, get_text( run, 0 ), nLen, run->style, &size); } *pAscent = run->style->tm.tmAscent; *pDescent = run->style->tm.tmDescent; size.cy = *pAscent + *pDescent; if (run->nFlags & MERF_TAB) { int pos = 0, i = 0, ppos, shift = 0; const PARAFORMAT2 *pFmt = ¶->fmt; if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) /* The horizontal gap shifts the tab positions to leave the gap. */ shift = pFmt->dxOffset * 2; do { if (i < pFmt->cTabCount) { /* Only one side of the horizontal gap is needed at the end of * the table row. */ if (i == pFmt->cTabCount -1) shift = shift >> 1; pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF); i++; } else { pos += lDefaultTab - (pos % lDefaultTab); } ppos = ME_twips2pointsX(c, pos); if (ppos > startx + run->pt.x) { size.cx = ppos - startx - run->pt.x; break; } } while(1);
/****************************************************************************** * ME_CharFromPointContext * * Returns a character position inside the run given a run-relative * pixel horizontal position. * * If closest is FALSE return the actual character * If closest is TRUE will round to the closest leading edge. * ie. if the second character is at pixel position 8 and third at 16 then for: * closest = FALSE cx = 0..7 return 0, cx = 8..15 return 1 * closest = TRUE cx = 0..3 return 0, cx = 4..11 return 1. */ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order) { ME_String *mask_text = NULL; WCHAR *str; int fit = 0; HGDIOBJ hOldFont; SIZE sz, sz2, sz3; if (!run->len || cx <= 0) return 0; if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { if (!closest || cx < run->nWidth / 2) return 0; return 1; } if (run->nFlags & MERF_GRAPHICS) { SIZE sz; ME_GetOLEObjectSize(c, run, &sz); if (!closest || cx < sz.cx / 2) return 0; return 1; } if (run->para->nFlags & MEPF_COMPLEX) { int cp, trailing; if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1; ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis, &cp, &trailing ); TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth, run->script_analysis.fRTL, run->script_analysis.fLogicalOrder); return closest ? cp + trailing : cp; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len ); str = mask_text->szData; } else str = get_text( run, 0 ); hOldFont = ME_SelectStyleFont(c, run->style); GetTextExtentExPointW(c->hDC, str, run->len, cx, &fit, NULL, &sz); if (closest && fit != run->len) { GetTextExtentPoint32W(c->hDC, str, fit, &sz2); GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3); if (cx >= (sz2.cx+sz3.cx)/2) fit = fit + 1; } ME_DestroyString( mask_text ); ME_UnselectStyleFont(c, run->style, hOldFont); return fit; }
/****************************************************************************** * ME_PointFromCharContext * * Returns a run-relative pixel position given a run-relative character * position (character offset) */ int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visual_order) { SIZE size; ME_String *mask_text = NULL; WCHAR *str; if (pRun->nFlags & MERF_GRAPHICS) { if (nOffset) ME_GetOLEObjectSize(c, pRun, &size); return nOffset != 0; } else if (pRun->nFlags & MERF_ENDPARA) { nOffset = 0; } if (pRun->para->nFlags & MEPF_COMPLEX) { int x; ScriptCPtoX( nOffset, FALSE, pRun->len, pRun->num_glyphs, pRun->clusters, pRun->vis_attrs, pRun->advances, &pRun->script_analysis, &x ); if (visual_order && pRun->script_analysis.fRTL) x = pRun->nWidth - x - 1; return x; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR(c->editor->cPasswordMask, pRun->len); str = mask_text->szData; } else str = get_text( pRun, 0 ); ME_GetTextExtent(c, str, nOffset, pRun->style, &size); ME_DestroyString( mask_text ); return size.cx; }
/****************************************************************************** * ME_PointFromCharContext * * Returns a run-relative pixel position given a run-relative character * position (character offset) */ int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visual_order) { SIZE size; ME_String *mask_text = NULL; WCHAR *str; if (pRun->nFlags & MERF_GRAPHICS) { if (nOffset) ME_GetOLEObjectSize(c, pRun, &size); return nOffset != 0; } else if (pRun->nFlags & MERF_ENDPARA) { nOffset = 0; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR(c->editor->cPasswordMask, pRun->len); str = mask_text->szData; } else str = get_text( pRun, 0 ); ME_GetTextExtent(c, str, nOffset, pRun->style, &size); ME_DestroyString( mask_text ); return size.cx; }
/****************************************************************************** * ME_GetRunSizeCommon * * Finds width, height, ascent and descent of a run, up to given character * (nLen). */ SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, int nLen, int startx, int *pAscent, int *pDescent) { SIZE size; int nMaxLen = run->len; if (nLen>nMaxLen) nLen = nMaxLen; /* FIXME the following call also ensures that TEXTMETRIC structure is filled * this is wasteful for MERF_NONTEXT runs, but that shouldn't matter * in practice */ if (c->editor->cPasswordMask) { ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen); ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size); ME_DestroyString(szMasked); } else { ME_GetTextExtent(c, get_text( run, 0 ), nLen, run->style, &size); } *pAscent = run->style->tm.tmAscent; *pDescent = run->style->tm.tmDescent; size.cy = *pAscent + *pDescent; if (run->nFlags & MERF_TAB) { int pos = 0, i = 0, ppos, shift = 0; PARAFORMAT2 *pFmt = para->pFmt; if (c->editor->bEmulateVersion10 && /* v1.0 - 3.0 */ pFmt->dwMask & PFM_TABLE && pFmt->wEffects & PFE_TABLE) /* The horizontal gap shifts the tab positions to leave the gap. */ shift = pFmt->dxOffset * 2; do { if (i < pFmt->cTabCount) { /* Only one side of the horizontal gap is needed at the end of * the table row. */ if (i == pFmt->cTabCount -1) shift = shift >> 1; pos = shift + (pFmt->rgxTabs[i]&0x00FFFFFF); i++; } else { pos += lDefaultTab - (pos % lDefaultTab); } ppos = ME_twips2pointsX(c, pos); if (ppos > startx + run->pt.x) { size.cx = ppos - startx - run->pt.x; break; } } while(1);
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; }
/****************************************************************************** * ME_CharFromPointContext * * Returns a character position inside the run given a run-relative * pixel horizontal position. * * If closest is FALSE return the actual character * If closest is TRUE will round to the closest leading edge. * ie. if the second character is at pixel position 8 and third at 16 then for: * closest = FALSE cx = 0..7 return 0, cx = 8..15 return 1 * closest = TRUE cx = 0..3 return 0, cx = 4..11 return 1. */ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order) { ME_String *mask_text = NULL; WCHAR *str; int fit = 0; HGDIOBJ hOldFont; SIZE sz, sz2, sz3; if (!run->len || cx <= 0) return 0; if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { if (!closest || cx < run->nWidth / 2) return 0; return 1; } if (run->nFlags & MERF_GRAPHICS) { SIZE sz; ME_GetOLEObjectSize(c, run, &sz); if (!closest || cx < sz.cx / 2) return 0; return 1; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len ); str = mask_text->szData; } else str = get_text( run, 0 ); hOldFont = ME_SelectStyleFont(c, run->style); GetTextExtentExPointW(c->hDC, str, run->len, cx, &fit, NULL, &sz); if (closest && fit != run->len) { GetTextExtentPoint32W(c->hDC, str, fit, &sz2); GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3); if (cx >= (sz2.cx+sz3.cx)/2) fit = fit + 1; } ME_DestroyString( mask_text ); ME_UnselectStyleFont(c, run->style, hOldFont); return fit; }
void ME_DestroyDisplayItem(ME_DisplayItem *item) { /* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */ if (item->type==diParagraph) { FREE_OBJ(item->member.para.pFmt); ME_DestroyString(item->member.para.text); } if (item->type==diRun) { if (item->member.run.ole_obj) ME_DeleteReObject(item->member.run.ole_obj); ME_ReleaseStyle(item->member.run.style); } FREE_OBJ(item); }
static void destroy_undo_item( struct undo_item *undo ) { switch( undo->type ) { case undo_insert_run: heap_free( undo->u.insert_run.str ); ME_ReleaseStyle( undo->u.insert_run.style ); break; case undo_split_para: ME_DestroyString( undo->u.split_para.eol_str ); break; default: break; } heap_free( undo ); }
static void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) { ME_Run *run = &rundi->member.run; ME_DisplayItem *start = ME_FindItemBack(rundi, diStartRow); int runofs = run->nCharOfs+para->nCharOfs; int nSelFrom, nSelTo; const WCHAR wszSpace[] = {' ', 0}; if (run->nFlags & MERF_HIDDEN) return; ME_GetSelection(c->editor, &nSelFrom, &nSelTo); /* Draw selected end-of-paragraph mark */ if (run->nFlags & MERF_ENDPARA && runofs >= nSelFrom && runofs < nSelTo) ME_DrawTextWithStyle(c, x, y, wszSpace, 1, run->style, NULL, 0, 1, c->pt.y + start->member.row.nYPos, start->member.row.nHeight); /* you can always comment it out if you need visible paragraph marks */ if (run->nFlags & (MERF_ENDPARA | MERF_TAB | MERF_CELL)) return; if (run->nFlags & MERF_GRAPHICS) ME_DrawGraphics(c, x, y, run, para, (runofs >= nSelFrom) && (runofs < nSelTo)); else { if (c->editor->cPasswordMask) { ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,ME_StrVLen(run->strText)); ME_DrawTextWithStyle(c, x, y, szMasked->szData, ME_StrVLen(szMasked), run->style, NULL, nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight); ME_DestroyString(szMasked); } else ME_DrawTextWithStyle(c, x, y, run->strText->szData, ME_StrVLen(run->strText), run->style, NULL, nSelFrom-runofs,nSelTo-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight); } }
BOOL ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars, BOOL bForce) { ME_Cursor c; int shift = 0; int totalChars = nChars; ME_DisplayItem *start_para; { /* Prevent deletion past last end of paragraph run. */ ME_DisplayItem *pTextEnd = editor->pBuffer->pLast; int nMaxChars = pTextEnd->member.para.prev_para->member.para.nCharOfs; nMaxChars += ME_FindItemBack(pTextEnd, diRun)->member.run.nCharOfs; nMaxChars -= nOfs; nChars = min(nChars, nMaxChars); } ME_CursorFromCharOfs(editor, nOfs, &c); start_para = ME_GetParagraph(c.pRun); if (!bForce) { ME_ProtectPartialTableDeletion(editor, nOfs, &nChars); if (nChars == 0) return FALSE; } while(nChars > 0) { ME_Run *run; ME_CursorFromCharOfs(editor, nOfs+nChars, &c); if (!c.nOffset && nOfs+nChars == (c.pRun->member.run.nCharOfs + ME_GetParagraph(c.pRun)->member.para.nCharOfs)) { /* We aren't deleting anything in this run, so we will go back to the * last run we are deleting text in. */ c.pRun = ME_FindItemBack(c.pRun, diRun); if (c.pRun->member.run.nFlags & MERF_ENDPARA) c.nOffset = c.pRun->member.run.nCR + c.pRun->member.run.nLF; else c.nOffset = c.pRun->member.run.strText->nLen; } run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = run->nCR + run->nLF; BOOL keepFirstParaFormat; if (!ME_FindItemFwd(c.pRun, diParagraph)) { return TRUE; } keepFirstParaFormat = (totalChars == nChars && nChars <= eollen && run->nCharOfs); if (!editor->bEmulateVersion10) /* v4.1 */ { ME_DisplayItem *next_para = ME_FindItemFwd(c.pRun, diParagraphOrEnd); ME_DisplayItem *this_para = next_para->member.para.prev_para; /* The end of paragraph before a table row is only deleted if there * is nothing else on the line before it. */ if (this_para == start_para && next_para->member.para.nFlags & MEPF_ROWSTART) { /* If the paragraph will be empty, then it should be deleted, however * it still might have text right now which would inherit the * MEPF_STARTROW property if we joined it right now. * Instead we will delete it after the preceding text is deleted. */ if (nOfs > this_para->member.para.nCharOfs) { /* Skip this end of line. */ nChars -= (eollen < nChars) ? eollen : nChars; continue; } keepFirstParaFormat = TRUE; } } ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun), keepFirstParaFormat); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); nChars -= (eollen < nChars) ? eollen : nChars; continue; } else { ME_Cursor cursor; int nCharsToDelete = min(nChars, c.nOffset); int i; c.nOffset -= nCharsToDelete; ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP; cursor = c; /* nChars is the number of characters that should be deleted from the PRECEDING runs (these BEFORE cursor.pRun) nCharsToDelete is a number of chars to delete from THIS run */ nChars -= nCharsToDelete; shift -= nCharsToDelete; TRACE("Deleting %d (remaning %d) chars at %d in '%s' (%d)\n", nCharsToDelete, nChars, c.nOffset, debugstr_w(run->strText->szData), run->strText->nLen); if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete) { /* undo = reinsert whole run */ /* nOfs is a character offset (from the start of the document to the current (deleted) run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) pUndo->di.member.run.nCharOfs = nOfs+nChars; } else { /* undo = reinsert partial run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) { ME_DestroyString(pUndo->di.member.run.strText); pUndo->di.member.run.nCharOfs = nOfs+nChars; pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete); } } TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen); TRACE("Shift value: %d\n", shift); ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete); /* update cursors (including c) */ for (i=-1; i<editor->nCursors; i++) { ME_Cursor *pThisCur = editor->pCursors + i; if (i == -1) pThisCur = &c; if (pThisCur->pRun == cursor.pRun) { if (pThisCur->nOffset > cursor.nOffset) { if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) pThisCur->nOffset = cursor.nOffset; else pThisCur->nOffset -= nCharsToDelete; assert(pThisCur->nOffset >= 0); assert(pThisCur->nOffset <= ME_StrVLen(run->strText)); } if (pThisCur->nOffset == ME_StrVLen(run->strText)) { pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); assert(pThisCur->pRun->type == diRun); pThisCur->nOffset = 0; } } } /* c = updated data now */ if (c.pRun == cursor.pRun) ME_SkipAndPropagateCharOffset(c.pRun, shift); else ME_PropagateCharOffset(c.pRun, shift); if (!ME_StrVLen(cursor.pRun->member.run.strText)) { TRACE("Removing useless run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } return TRUE; }
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars) { ME_Cursor c; int shift = 0; while(nChars > 0) { ME_Run *run; ME_CursorFromCharOfs(editor, nOfs, &c); run = &c.pRun->member.run; if (run->nFlags & MERF_ENDPARA) { int eollen = run->nCR + run->nLF; if (!ME_FindItemFwd(c.pRun, diParagraph)) { return; } ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun)); /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ ME_CheckCharOffsets(editor); nChars -= (eollen < nChars) ? eollen : nChars; continue; } else { ME_Cursor cursor; int nIntendedChars = nChars; int nCharsToDelete = nChars; int i; int loc = c.nOffset; ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags |= MEPF_REWRAP; cursor = c; ME_StrRelPos(run->strText, loc, &nChars); /* nChars is the number of characters that should be deleted from the FOLLOWING runs (these AFTER cursor.pRun) nCharsToDelete is a number of chars to delete from THIS run */ nCharsToDelete -= nChars; shift -= nCharsToDelete; TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n", nCharsToDelete, nIntendedChars, nChars, c.nOffset, debugstr_w(run->strText->szData), run->strText->nLen); if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete) { /* undo = reinsert whole run */ /* nOfs is a character offset (from the start of the document to the current (deleted) run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) pUndo->di.member.run.nCharOfs = nOfs; } else { /* undo = reinsert partial run */ ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); if (pUndo) { ME_DestroyString(pUndo->di.member.run.strText); pUndo->di.member.run.nCharOfs = nOfs; pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete); } } TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen); TRACE("Shift value: %d\n", shift); ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete); /* update cursors (including c) */ for (i=-1; i<editor->nCursors; i++) { ME_Cursor *pThisCur = editor->pCursors + i; if (i == -1) pThisCur = &c; if (pThisCur->pRun == cursor.pRun) { if (pThisCur->nOffset > cursor.nOffset) { if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) pThisCur->nOffset = cursor.nOffset; else pThisCur->nOffset -= nCharsToDelete; assert(pThisCur->nOffset >= 0); assert(pThisCur->nOffset <= ME_StrVLen(run->strText)); } if (pThisCur->nOffset == ME_StrVLen(run->strText)) { pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); assert(pThisCur->pRun->type == diRun); pThisCur->nOffset = 0; } } } /* c = updated data now */ if (c.pRun == cursor.pRun) ME_SkipAndPropagateCharOffset(c.pRun, shift); else ME_PropagateCharOffset(c.pRun, shift); if (!ME_StrVLen(cursor.pRun->member.run.strText)) { TRACE("Removing useless run\n"); ME_Remove(cursor.pRun); ME_DestroyDisplayItem(cursor.pRun); } shift = 0; /* ME_CheckCharOffsets(editor); */ continue; } } }