📄 editor.c
字号:
break;
}
nStart += para->member.para.nCharOfs + pCurItem->member.run.nCharOfs;
if (chrgText)
{
chrgText->cpMin = nStart;
chrgText->cpMax = nStart + nLen;
}
TRACE("found at %d-%d\n", nStart, nStart + nLen);
return nStart;
}
if (nCurStart + nMatched == ME_StrLen(pCurItem->member.run.strText))
{
pCurItem = ME_FindItemFwd(pCurItem, diRun);
para = ME_GetParagraph(pCurItem);
nCurStart = -nMatched;
}
}
if (pCurItem)
wLastChar = pCurItem->member.run.strText->szData[nCurStart + nMatched];
else
wLastChar = ' ';
nStart++;
if (nStart == ME_StrLen(item->member.run.strText))
{
item = ME_FindItemFwd(item, diRun);
para = ME_GetParagraph(item);
nStart = 0;
}
}
}
else /* Backward search */
{
/* If possible, find the character after where the search ends */
if ((flags & FR_WHOLEWORD) && nMax < nTextLen - 1)
{
nEnd = nMax + 1;
item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
if (!item)
{
if (chrgText)
chrgText->cpMin = chrgText->cpMax = -1;
return -1;
}
wLastChar = item->member.run.strText->szData[nEnd];
}
nEnd = nMax;
item = ME_FindItemAtOffset(editor, diRun, nEnd, &nEnd);
if (!item)
{
if (chrgText)
chrgText->cpMin = chrgText->cpMax = -1;
return -1;
}
para = ME_GetParagraph(item);
while (item
&& para->member.para.nCharOfs + item->member.run.nCharOfs + nEnd - nLen >= nMin)
{
ME_DisplayItem *pCurItem = item;
int nCurEnd = nEnd;
int nMatched = 0;
if (nCurEnd - nMatched == 0)
{
pCurItem = ME_FindItemBack(pCurItem, diRun);
para = ME_GetParagraph(pCurItem);
nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
}
while (pCurItem && ME_CharCompare(pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1], text[nLen - nMatched - 1], (flags & FR_MATCHCASE)))
{
if ((flags & FR_WHOLEWORD) && isalnumW(wLastChar))
break;
nMatched++;
if (nMatched == nLen)
{
ME_DisplayItem *pPrevItem = pCurItem;
int nPrevEnd = nCurEnd;
WCHAR wPrevChar;
/* Check to see if previous character is a whitespace */
if (flags & FR_WHOLEWORD)
{
if (nPrevEnd - nMatched == 0)
{
pPrevItem = ME_FindItemBack(pCurItem, diRun);
if (pPrevItem)
nPrevEnd = ME_StrLen(pPrevItem->member.run.strText) + nMatched;
}
if (pPrevItem)
wPrevChar = pPrevItem->member.run.strText->szData[nPrevEnd - nMatched - 1];
else
wPrevChar = ' ';
if (isalnumW(wPrevChar))
break;
}
nStart = para->member.para.nCharOfs + pCurItem->member.run.nCharOfs + nCurEnd - nMatched;
if (chrgText)
{
chrgText->cpMin = nStart;
chrgText->cpMax = nStart + nLen;
}
TRACE("found at %d-%d\n", nStart, nStart + nLen);
return nStart;
}
if (nCurEnd - nMatched == 0)
{
pCurItem = ME_FindItemBack(pCurItem, diRun);
/* Don't care about pCurItem becoming NULL here; it's already taken
* care of in the exterior loop condition */
para = ME_GetParagraph(pCurItem);
nCurEnd = ME_StrLen(pCurItem->member.run.strText) + nMatched;
}
}
if (pCurItem)
wLastChar = pCurItem->member.run.strText->szData[nCurEnd - nMatched - 1];
else
wLastChar = ' ';
nEnd--;
if (nEnd < 0)
{
item = ME_FindItemBack(item, diRun);
para = ME_GetParagraph(item);
nEnd = ME_StrLen(item->member.run.strText);
}
}
}
TRACE("not found\n");
if (chrgText)
chrgText->cpMin = chrgText->cpMax = -1;
return -1;
}
static BOOL
ME_KeyDown(ME_TextEditor *editor, WORD nKey)
{
BOOL ctrl_is_down = GetKeyState(VK_CONTROL) & 0x8000;
BOOL shift_is_down = GetKeyState(VK_SHIFT) & 0x8000;
switch (nKey)
{
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
ME_ArrowKey(editor, nKey, shift_is_down, ctrl_is_down);
return TRUE;
case VK_BACK:
case VK_DELETE:
/* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */
if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY)
return FALSE;
if (ME_IsSelection(editor))
ME_DeleteSelection(editor);
else if (nKey == VK_DELETE || ME_ArrowKey(editor, VK_LEFT, FALSE, FALSE))
ME_DeleteTextAtCursor(editor, 1, 1);
else
return TRUE;
ME_CommitUndo(editor);
ME_UpdateRepaint(editor);
ME_SendRequestResize(editor, FALSE);
return TRUE;
default:
if (ctrl_is_down)
{
if (nKey == 'W')
{
CHARFORMAT2W chf;
char buf[2048];
chf.cbSize = sizeof(chf);
ME_GetSelectionCharFormat(editor, &chf);
ME_DumpStyleToBuf(&chf, buf);
MessageBoxA(NULL, buf, "Style dump", MB_OK);
}
if (nKey == 'Q')
{
ME_CheckCharOffsets(editor);
}
}
}
return FALSE;
}
static BOOL ME_ShowContextMenu(ME_TextEditor *editor, int x, int y)
{
CHARRANGE selrange;
HMENU menu;
int seltype = 0;
if(!editor->lpOleCallback)
return FALSE;
ME_GetSelection(editor, (int *)&selrange.cpMin, (int *)&selrange.cpMax);
if(selrange.cpMin == selrange.cpMax)
seltype |= SEL_EMPTY;
else
{
/* FIXME: Handle objects */
seltype |= SEL_TEXT;
if(selrange.cpMax-selrange.cpMin > 1)
seltype |= SEL_MULTICHAR;
}
if(SUCCEEDED(IRichEditOleCallback_GetContextMenu(editor->lpOleCallback, seltype, NULL, &selrange, &menu)))
{
TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, GetParent(editor->hWnd), NULL);
DestroyMenu(menu);
}
return TRUE;
}
ME_TextEditor *ME_MakeEditor(HWND hWnd) {
ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor);
HDC hDC;
int i;
ed->hWnd = hWnd;
ed->bEmulateVersion10 = FALSE;
ed->pBuffer = ME_MakeText();
hDC = GetDC(hWnd);
ME_MakeFirstParagraph(hDC, ed->pBuffer);
ReleaseDC(hWnd, hDC);
ed->bCaretShown = FALSE;
ed->nCursors = 2;
ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors);
ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
ed->pCursors[0].nOffset = 0;
ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
ed->pCursors[1].nOffset = 0;
ed->nLastTotalLength = ed->nTotalLength = 0;
ed->nUDArrowX = -1;
ed->nSequence = 0;
ed->rgbBackColor = -1;
ed->hbrBackground = GetSysColorBrush(COLOR_WINDOW);
ed->bCaretAtEnd = FALSE;
ed->nEventMask = 0;
ed->nModifyStep = 0;
ed->nTextLimit = TEXT_LIMIT_DEFAULT;
ed->pUndoStack = ed->pRedoStack = ed->pUndoStackBottom = NULL;
ed->nUndoStackSize = 0;
ed->nUndoLimit = STACK_SIZE_DEFAULT;
ed->nUndoMode = umAddToUndo;
ed->nParagraphs = 1;
ed->nLastSelStart = ed->nLastSelEnd = 0;
ed->pLastSelStartPara = ed->pLastSelEndPara = ME_FindItemFwd(ed->pBuffer->pFirst, diParagraph);
ed->nZoomNumerator = ed->nZoomDenominator = 0;
ed->bRedraw = TRUE;
ed->bHideSelection = FALSE;
ed->nInvalidOfs = -1;
ed->pfnWordBreak = NULL;
ed->lpOleCallback = NULL;
ed->mode = TM_RICHTEXT | TM_MULTILEVELUNDO | TM_MULTICODEPAGE;
ed->AutoURLDetect_bEnable = FALSE;
ed->bHaveFocus = FALSE;
GetClientRect(hWnd, &ed->rcFormat);
for (i=0; i<HFONT_CACHE_SIZE; i++)
{
ed->pFontCache[i].nRefs = 0;
ed->pFontCache[i].nAge = 0;
ed->pFontCache[i].hFont = NULL;
}
ME_CheckCharOffsets(ed);
if (GetWindowLongW(hWnd, GWL_STYLE) & ES_PASSWORD)
ed->cPasswordMask = '*';
else
ed->cPasswordMask = 0;
return ed;
}
typedef struct tagME_GlobalDestStruct
{
HGLOBAL hData;
int nLength;
} ME_GlobalDestStruct;
static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
{
ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
int i;
WORD *pSrc, *pDest;
cb = cb >> 1;
pDest = (WORD *)lpBuff;
pSrc = (WORD *)GlobalLock(pData->hData);
for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
pDest[i] = pSrc[pData->nLength+i];
}
pData->nLength += i;
*pcb = 2*i;
GlobalUnlock(pData->hData);
return 0;
}
static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
{
ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
int i;
BYTE *pSrc, *pDest;
pDest = lpBuff;
pSrc = (BYTE *)GlobalLock(pData->hData);
for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
pDest[i] = pSrc[pData->nLength+i];
}
pData->nLength += i;
*pcb = i;
GlobalUnlock(pData->hData);
return 0;
}
void ME_DestroyEditor(ME_TextEditor *editor)
{
ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
ME_DisplayItem *p = pFirst, *pNext = NULL;
int i;
ME_ClearTempStyle(editor);
ME_EmptyUndoStack(editor);
while(p) {
pNext = p->next;
ME_DestroyDisplayItem(p);
p = pNext;
}
ME_ReleaseStyle(editor->pBuffer->pDefaultStyle);
for (i=0; i<HFONT_CACHE_SIZE; i++)
{
if (editor->pFontCache[i].hFont)
DeleteObject(editor->pFontCache[i].hFont);
}
DeleteObject(editor->hbrBackground);
if(editor->lpOleCallback)
IUnknown_Release(editor->lpOleCallback);
FREE_OBJ(editor->pBuffer);
FREE_OBJ(editor->pCursors);
FREE_OBJ(editor);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
TRACE("\n");
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
me_heap = HeapCreate (0, 0x10000, 0);
if (!ME_RegisterEditorClass(hinstDLL)) return FALSE;
LookupInit();
break;
case DLL_PROCESS_DETACH:
UnregisterClassW(RichEdit20W, 0);
UnregisterClassW(RichEdit50W, 0);
UnregisterClassA("RichEdit20A", 0);
UnregisterClassA("RichEdit50A", 0);
if (ME_ListBoxRegistered)
UnregisterClassW(REListBox20W, 0);
if (ME_ComboBoxRegistered)
UnregisterClassW(REComboBox20W, 0);
LookupCleanup();
HeapDestroy (me_heap);
me_heap = NULL;
break;
}
return TRUE;
}
#define UNSUPPORTED_MSG(e) \
case e: \
FIXME(#e ": stub\n"); \
return DefWindowProcW(hWnd, msg, wParam, lParam);
static const char * const edit_messages[] = {
"EM_GETSEL",
"EM_SETSEL",
"EM_GETRECT",
"EM_SETRECT",
"EM_SETRECTNP",
"EM_SCROLL",
"EM_LINESCROLL",
"EM_SCROLLCARET",
"EM_GETMODIFY",
"EM_SETMODIFY",
"EM_GETLINECOUNT",
"EM_LINEINDEX",
"EM_SETHANDLE",
"EM_GETHANDLE",
"EM_GETTHUMB",
"EM_UNKNOWN_BF",
"EM_UNKNOWN_C0",
"EM_LINELENGTH",
"EM_REPLACESEL",
"EM_UNKNOWN_C3",
"EM_GETLINE",
"EM_LIMITTEXT",
"EM_CANUNDO",
"EM_UNDO",
"EM_FMTLINES",
"EM_LINEFROMCHAR",
"EM_UNKNOWN_CA",
"EM_SETTABSTOPS",
"EM_SETPASSWORDCHAR",
"EM_EMPTYUNDOBUFFER",
"EM_GETFIRSTVISIBLELINE",
"EM_SETREADONLY",
"EM_SETWORDBREAKPROC",
"EM_GETWORDBREAKPROC",
"EM_GETPASSWORDCHAR",
"EM_SETMARGINS",
"EM_GETMARGINS",
"EM_GETLIMITTEXT",
"EM_POSFROMCHAR",
"EM_CHARFROMPOS"
};
static const char * const richedit_messages[] = {
"EM_CANPASTE",
"EM_DISPLAYBAND",
"EM_EXGETSEL",
"EM_EXLIMITTEXT",
"EM_EXLINEFROMCHAR",
"EM_EXSETSEL",
"EM_FINDTEXT",
"EM_FORMATRANGE",
"EM_GETCHARFORMAT",
"EM_GETEVENTMASK",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -