📄 textedit.c
字号:
*pIns = txtdoc->lnsep;
*(pIns + len) = '\0';
return newnode;
}
/*
* textdoc_insert_string_ex :
* inserts a text string(not including line seperator) into
* the text buffer at the specified insertion point, or makes
* some special operations (inserts line sep, del, bs, ...)
* params : enode - the specified node to operate on
* insert_pos - the designed insert position
* newtext - text to insert
* len - len of text to insert or remove
*/
static int
textdoc_insert_string_ex (TextDoc *txtdoc, TextNode *enode, int insert_pos,
const char* newtext, int len)
{
HWND hWnd = (HWND)txtdoc->fn_data;
TextNode *curnode, *newnode;
StrBuffer *strbuff;
unsigned char *pIns;
#ifdef _UNDO_SUPPORT
//teUndoBackup (txtdoc);
#endif
#ifdef _SELECT_SUPPORT
if (!enode && txtdoc->selection.curNode) {
insert_pos = delete_selection (txtdoc);
if (len == 1 && (*newtext == '\b' || *newtext == 127) )
return insert_pos;
else {
txtdoc->insert.pos_lnOff = insert_pos;
}
}
#endif
if (!enode) { //operates on the current insertion point
curnode = txtdoc->insert.curNode;
insert_pos = txtdoc->insert.pos_lnOff;
}
else {
curnode = enode;
}
strbuff = &curnode->content;
pIns = strbuff->string + insert_pos;
if ( len == 1 || (len == 2 && strcmp(newtext, CH_RN) == 0) ) {
if ( *newtext == txtdoc->lnsep || (strncmp(newtext, CH_RN, 2) == 0) ) {
/* add a new line */
newnode = insert_ln_sep (txtdoc, curnode,
insert_pos, len == 2);
if (!enode) {
txtdoc->insert.curNode = newnode;
insert_pos = 0;
}
return insert_pos;
}
/* len == 1, other cases */
switch (*newtext) {
case '\b': //BS
if (pIns == strbuff->string) { //line head
TextNode *node;
if (BE_FIRST_NODE(curnode))
return insert_pos;
node = TXTNODE_PREV(curnode);
/* adds the previous line at the beginning of the current line */
insert_pos += textdoc_insert_string_ex ( txtdoc, enode, insert_pos,
node->content.string, node->content.txtlen-1 );
/* deletes the previous line */
txtDelNode (txtdoc, node);
txtdoc->change_fn (txtdoc, FALSE);
return insert_pos;
}
len = - CHLENPREV(strbuff->string, pIns);
break;
case 127: //DEL
if (*pIns == '\0')
return insert_pos;
if ( *pIns == txtdoc->lnsep || is_rn_sep(pIns) ) {
TextNode *node = TXTNODE_NEXT(curnode);
int oldpos = insert_pos;
if (node->content.string[0] != txtdoc->lnsep &&
!is_rn_sep(node->content.string)) {
*pIns = '\0';
curnode->content.txtlen = pIns - curnode->content.string;
textdoc_insert_string_ex ( txtdoc, enode, insert_pos,
node->content.string, node->content.txtlen );
insert_pos = oldpos;
}
txtDelNode (txtdoc, node);
return insert_pos;
}
else {
int chlen = CHLENNEXT( pIns, (curnode->content.txtlen +
strbuff->string - pIns) );
pIns += chlen;
insert_pos += chlen;
len = -chlen;
}
break;
}
}
insert_string (txtdoc, curnode, insert_pos, newtext, len);
insert_pos += len;
return insert_pos;
}
/*
* textdoc_insert_text : insert a multi-line text at the insertion point
*/
static int
textdoc_insert_text (TextDoc *txtdoc, const char* newtext, int len)
{
const char *pLine, *ptmp;
TextNode *node, *newnode, *nextnode;
int leftlen = len;
if (!txtdoc || !newtext || len < 0) return -1;
pLine = ptmp = newtext;
/* insert first strings */
while (*ptmp != txtdoc->lnsep && *ptmp != '\0' && leftlen > 0) {
ptmp ++;
leftlen --;
}
textdoc_insert_string (txtdoc, pLine, ptmp-pLine);
if (*ptmp == '\0' || leftlen <= 0)
return 0;
node = txtdoc->insert.curNode;
/* make next node */
if (ptmp > pLine && is_rn_sep(ptmp - 1)) {
textdoc_insert_string (txtdoc, CH_RN, 2);
}
else {
textdoc_insert_string (txtdoc, &txtdoc->lnsep, 1);
}
ptmp ++;
leftlen --;
pLine = ptmp;
nextnode = TXTNODE_NEXT (node);
/* insert lines */
newnode = node;
while (leftlen > 0 && *ptmp != '\0') {
if (*ptmp == txtdoc->lnsep) {
newnode = txtAddNode (txtdoc, pLine, ptmp-pLine+1, newnode);
pLine = ptmp + 1;
}
ptmp ++;
leftlen --;
}
#ifdef _SELECT_SUPPORT
txtdoc->selection.curNode =NULL;
txtdoc->selection.pos_lnOff = 0;
#endif
/* insert last strings */
//FIXME
if (!nextnode) {
fprintf (stderr, "Warning : nextnode is NULL!\n");
//set_current_node (txtdoc, LASTNODE(txtdoc), FALSE, TRUE);
}
else {
set_current_node (txtdoc, nextnode, FALSE, TRUE);
textdoc_insert_string (txtdoc, pLine, ptmp-pLine);
}
return 0;
}
/* ---------------------------------------------------------------------------- */
#ifdef _TITLE_SUPPORT
static int teGetTitleIndent (HWND hWnd, PTEDATA ptedata, HDC hdc)
{
SIZE txtsize = {0, 0};
if (ptedata->title)
GetTextExtent(hdc, ptedata->title, strlen(ptedata->title), &txtsize);
return txtsize.cx;
}
#endif
/* paint textedit base lines */
static void tePaint(HWND hWnd, HDC hdc, RECT *rcDraw)
{
RECT *rc = rcDraw;
PTEDATA ptedata;
int h, indent = 0;
ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd);
h = ptedata->nLineHeight - 1;
if (GetWindowStyle(hWnd) & ES_BASELINE) {
SetPenColor (hdc, GetWindowElementColorEx (hWnd, FGC_CONTROL_NORMAL));
while (h < RECTHP(rc)) {
#ifdef _TITLE_SUPPORT
indent = (h == ptedata->nLineHeight - 1) ? ptedata->titleIndent : 0;
#endif
DrawHDotLine (hdc, rc->left + indent, rc->top+h, RECTWP(rc)-indent);
h += ptedata->nLineHeight;
}
}
}
static void setup_dc (HWND hWnd, HDC hdc, BOOL bSel)
{
if (!bSel) {
SetBkMode (hdc, BM_TRANSPARENT);
SetTextColor (hdc, GetWindowElementColorEx (hWnd, GetWindowStyle(hWnd)&WS_DISABLED?
FGC_CONTROL_DISABLED:FGC_CONTROL_NORMAL));
}
else {
SetBkMode (hdc, BM_OPAQUE);
SetBkColor (hdc, GetWindowElementColorEx (hWnd, BKC_HILIGHT_NORMAL));
SetTextColor (hdc, GetWindowElementColorEx (hWnd, FGC_HILIGHT_NORMAL));
}
}
/*
* teDrawItem : draw text node/item, including multi lines in wrap mode
*/
static void teDrawItem (HWND hWnd, HSVITEM hsvi, HDC hdc, RECT *rcDraw)
{
RECT rcTxt = *rcDraw;
TextNode *node;
char *content;
int txtlen, outlen, indent;
UINT format;
PTEDATA ptedata;
TextDoc *txtdoc;
#ifdef _SELECT_SUPPORT
int i, pos_start, pos_end, outchars, line_nr, outw, selout = 0;
DTFIRSTLINE fl;
#endif
unsigned char chln = 0;
ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd);
txtdoc = &ptedata->txtdoc;
node = (TextNode*) scrollview_get_item_adddata(hsvi);
content = node->content.string;
txtlen = node->content.txtlen;
indent = teGetLineIndent (ptedata, node);
if (txtlen <= 0 && indent <= 0)
return;
setup_dc (hWnd, hdc, FALSE);
format = TE_FORMAT;
if (BE_WRAP(hWnd))
format |= DT_WORDBREAK;
SetTextAboveLineExtra (hdc, ptedata->nLineAboveH);
SetTextBellowLineExtra (hdc, ptedata->nLineBaseH);
#ifdef _TITLE_SUPPORT
/* draw title */
if (ptedata->title && indent > 0)
DrawText (hdc, ptedata->title, strlen(ptedata->title), &rcTxt, format);
#endif
outlen = txtlen;
if (outlen > 0 && content[outlen-1] == txtdoc->lnsep) {
outlen --;
if (txtdoc->lnsep == '\n' && content[outlen-1] == '\r')
outlen --;
if (ptedata->lnChar) {
outlen ++;
chln = content[outlen-1];
content[outlen-1] = ptedata->lnChar;
}
}
/* draw not selected node line text */
#ifdef _SELECT_SUPPORT
if (!textnode_is_selected(txtdoc, node)) {
DrawTextEx (hdc, content, outlen, &rcTxt, indent, format);
if (chln)
content[outlen-1] = chln;
return;
}
/* draw selected node */
get_selection_points (ptedata, node, &pos_start, &pos_end);
line_nr = RECTH(rcTxt) / ptedata->nLineHeight;
for (i = 0; i < line_nr; i++) {
int startx, starty;
if (i > 0) indent = 0;
/* calc line info */
DrawTextEx2(hdc, content, outlen, &rcTxt, indent, format, &fl);
startx = rcTxt.left;
starty = rcTxt.top;
outw = indent;
outchars = 0;
if (pos_start > 0) { //first: not selected section
setup_dc (hWnd, hdc, FALSE);
outchars = MIN(pos_start,fl.nr_chars);
outw += TabbedTextOutLen (hdc, startx + outw, starty, content, outchars);
content += outchars;
}
if (pos_start < fl.nr_chars && pos_end > 0) { //second: selected section
outchars = MIN(pos_end, fl.nr_chars) - MAX(pos_start,0);
if (!ptedata->drawSelected) {
setup_dc (hWnd, hdc, TRUE);
outw += TabbedTextOutLen (hdc, startx + outw, starty, content, outchars);
}
else {
SetBkMode (hdc, BM_OPAQUE);
outw += ptedata->drawSelected(hWnd, hdc, startx+outw, starty, content, outchars, selout);
}
content += outchars;
selout += outchars;
}
if (pos_end < fl.nr_chars) { //third: not selected section
setup_dc (hWnd, hdc, FALSE);
outchars = fl.nr_chars - MAX(pos_end, 0);
outw += TabbedTextOutLen (hdc, startx + outw, starty, content, outchars);
content += outchars;
}
pos_start -= fl.nr_chars;
pos_end -= fl.nr_chars;
outlen -= fl.nr_chars;
rcTxt.top += ptedata->nLineHeight;
}
#else
DrawTextEx (hdc, content, outlen, &rcTxt, indent, format);
#endif
if (chln)
content[outlen-1] = chln;
}
/*
static void teDestroyItem (HWND hWnd, HSVITEM hsvi)
{
TextNode *node = (TextNode*)scrollview_get_item_adddata(hsvi);
textnode_destroy (node);
}
*/
/* --------------------------- size and position calculation ---------------- */
/* sets the current caret position in the virtual content window */
static void mySetCaretPos (HWND hWnd, int x, int y)
{
PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd);
if (x >= 0)
ptedata->caret_x = x;
else
x = ptedata->caret_x;
if (y >= 0)
ptedata->caret_y = y;
else
y = ptedata->caret_y;
if (scrolled_content_to_visible (ptescr, &x, &y) < 0) {
HideCaret (hWnd);
}
else {
scrolled_visible_to_window (ptescr, &x, &y);
if (ptedata->flags & TEST_FOCUSED) {
ShowCaret (hWnd);
ActiveCaret (hWnd);
}
SetCaretPos (hWnd, x, y);
}
}
static void textedit_set_svlist (HWND hWnd, PSCRDATA pscrdata, BOOL visChanged)
{
PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd);
/* reset caret pos in the real window */
if (ptedata->flags & TEST_FOCUSED)
mySetCaretPos (hWnd, -1, -1);
//FIXME: should be ScrollWindow ?
//InvalidateRect (hWnd, NULL, TRUE);
scrollview_set_svlist (hWnd, pscrdata, visChanged);
}
/*
* set_caret_pos : Sets the caret/selection position in a node according to
* x,y values
*/
static int
set_caret_pos (HWND hWnd, PTEDATA ptedata, TextNode *node, int x, int y, BOOL bSel)
{
TextDoc *txtdoc = &ptedata->txtdoc;
const unsigned char *string = node->content.string;
const unsigned char *pLine = string;
int txtlen = node->content.txtlen;
int line, indent, h = 0, out_chars, ln_chars;
RECT rc;
SIZE txtsize;
DTFIRSTLINE fl;
HDC hdc;
indent = teGetLineIndent (ptedata, node);
line = y / ptedata->nLineHeight;
if (!bSel)
h = ptedata->curItemY + line * ptedata->nLineHeight;
#ifdef _SELECT_SUPPORT
else
h = ptedata->selItemY + line * ptedata->nLineHeight;
#endif
if (txtlen == 0 || string[0] == txtdoc->lnsep || is_rn_sep(string)) {
if (!bSel) {
txtdoc->insert.pos_lnOff = 0;
mySetCaretPos (hWnd, 0 + indent, h + ptedata->nLineAboveH);
}
#ifdef _SELECT_SUPPORT
else {
txtdoc->selection.pos_lnOff = 0;
mySetSelPos (0 + indent, h + ptedata->nLineAboveH);
}
#endif
return 0;
}
//FIXME
hdc = GetClientDC (hWnd);
if (NODE_LINENR(node) > 1) { /* multi line case */
int i = 0;
rc.left = 0;
rc.top = 0;
rc.right = scrolled_get_contwidth(ptescr);
rc.bottom = ptedata->nLineHeight;
i = 0;
while (1) {
if (i > 0) indent = 0;
DrawTextEx2 (hdc, pLine, txtlen, &rc, indent,
TE_FORMAT | DT_WORDBREAK | DT_CALCRECT, &fl);
if (i == line)
break;
pLine += fl.nr_chars;
txtlen -= fl.nr_chars;
i++;
}
ln_chars = fl.nr_chars;
}
else {
pLine = string;
ln_chars = txtlen;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -