/////////////////////////////////////////////////////////////////////////////// // // IMPORTANT NOTICE // // The following open source license statement does not apply to any // entity in the Exception List published by FMSoft. // // For more information, please visit: // // https://www.fmsoft.cn/exception-list // ////////////////////////////////////////////////////////////////////////////// /* * This file is part of MiniGUI, a mature cross-platform windowing * and Graphics User Interface (GUI) support system for embedded systems * and smart IoT devices. * * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. * Copyright (C) 1998~2002, WEI Yongming * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Or, * * As this program is a library, any link to this program must follow * GNU General Public License version 3 (GPLv3). If you cannot accept * GPLv3, you need to be licensed from FMSoft. * * If you have got a commercial license of this program, please use it * under the terms and conditions of the commercial license. * * For more information about the commercial license, please refer to * . */ /* ** textedit.c: text edit control ** ** Create date: 2004/03/01 ** ** Note: ** the textedit control is written from scratch ** to replace the buggy medit control. ** ** The textedit control inherits scrollview. ** ** TODO: ** tab */ #include #include #include #include #include "common.h" #if defined(_MGCTRL_TEXTEDIT) && !defined(_MGCTRL_TEXTEDIT_USE_NEW_IMPL) #include "minigui.h" #include "gdi.h" #include "window.h" #include "ctrl/ctrlhelper.h" #include "ctrl/edit.h" #include "ctrl/textedit.h" #include "cliprect.h" #include "internals.h" #include "ctrlclass.h" #include "ctrlmisc.h" #include "scrolled.h" #include "scrollview_impl.h" #include "text.h" #include "textedit_impl.h" #ifdef _UNDO_SUPPORT static void teUndoBackup (TextDoc *txtoc); #endif #define TEXTEDIT_BUFDC 1 #undef _TEXTEDIT_DEBUG /* #define _TEXTEDIT_DEBUG */ #ifndef TEXTEDIT_BUFDC #define GETDC(hWnd) GetClientDC(hWnd) #define RELEASEDC(hWnd, hdc) ReleaseDC(hdc) #else inline HDC GETDC(HWND hWnd) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); if (!ptedata) return GetClientDC(hWnd); SelectClipRect(ptedata->buf_dc, NULL); return ptedata->buf_dc; } inline void RELEASEDC(HWND hWnd, HDC hdc) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); if (ptedata && ptedata->buf_dc != hdc) ReleaseDC(hdc); } #endif #ifdef _TEXTEDIT_DEBUG static void dump_text (TextDoc *txtdoc, BOOL bSel) { list_t *me; TextNode *node; SIZE txtsize; HWND hWnd = (HWND)txtdoc->fn_data; HDC hdc; printf ("\n\n\n\n"); printf ("------------------------------------------------------\n"); list_for_each (me, &txtdoc->queue) { node = list_entry (me, TextNode, list); /* if (scrollview_is_item_selected ((HSVITEM)node->addData)) { printf ("sv select---<< %s\n", node->content.string); } */ #ifdef _SELECT_SUPPORT if (bSel && textnode_is_selected(txtdoc, node)) { printf ("%d:\n", node->content.txtlen); printf ("%s----->", node->content.string); } #endif } hdc = GETDC (hWnd); GetTabbedTextExtent(hdc, "\t", 1, &txtsize); RELEASEDC (hWnd, hdc); printf ("tab size = %d\n", txtsize.cx); } static void print_selected (TextDoc *txtdoc) { dump_text (txtdoc, TRUE); } #endif /* ------------------------------ text document/buffer ------------------------ */ /* * set_current_node : Set a node as the current insertion/selection node, * must be called when the current node is/will be changed. * Params : newnode - the new node with insertion/selection point * bSel - insertion or selection * bChange - Whether to call change function * Return : TRUE on changed, FALSE otherwise. */ static BOOL set_current_node (TextDoc *txtdoc, TextNode *newnode, BOOL bSel, BOOL bChange) { TextMark *mark; TextNode *oldnode; mark = GETMARK(bSel); oldnode = mark->curNode; if (newnode == oldnode) return FALSE; mark->curNode = newnode; mark->pos_lnOff = 0; /* called when the current insertion node is changed */ if (bChange && txtdoc->change_fn) txtdoc->change_fn (txtdoc, bSel); return TRUE; } /* * textnode_create: creat a new text node and initialize it with text */ static TextNode* textnode_create (TextDoc *txtdoc, const char *line, int len) { TextNode *newnode; if ( !(newnode = textnode_alloc ()) ) return NULL; /* create a new blank line */ if (!line || len < 0) len = 0; if ( !(testr_alloc (&newnode->content, len, txtdoc->nBlockSize)) ) { textnode_free (newnode); return NULL; } testr_setstr (&newnode->content, line, len); newnode->addData = 0; return newnode; } /* * textnode_destroy: destroy a text node */ static void textnode_destroy (TextNode *node) { if (node) { list_del (&node->list); testr_free (&node->content); textnode_free (node); } } /* * textdoc_free : free TextDoc nodes * Description : only changes the status fields of a TextDoc object, does not * affect the properties. */ static void textdoc_free (TextDoc *txtdoc) { TextNode *node; if (!txtdoc) return; while (!list_empty(&txtdoc->queue)) { node = list_entry (txtdoc->queue.next, TextNode, list); textnode_destroy (node); } txtdoc->insert.pos_lnOff = 0; txtdoc->insert.curNode = NULL; #ifdef _SELECT_SUPPORT txtdoc->selection.curNode = 0; txtdoc->selection.pos_lnOff = 0; #endif } /* * txtAddNode : add a textnode after a specified node * params : node - the previous text node, if NULL, the new node will be * inserted at the tail. */ static TextNode* txtAddNode (TextDoc *txtdoc, const char*pLine, int len, TextNode *node) { TextNode *newnode; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2((HWND)txtdoc->fn_data); if ( !(newnode = textnode_create (txtdoc, pLine, len)) ) return NULL; if (node) list_add (&newnode->list, &node->list); else list_add_tail (&newnode->list, &txtdoc->queue); if (txtdoc->init_fn) txtdoc->init_fn(txtdoc, newnode, node); if (newnode) ptedata->curlen += newnode->content.txtlen; return newnode; } /* * txtDelNode : deletes a text node */ static void txtDelNode (TextDoc *txtdoc, TextNode *node) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2((HWND)txtdoc->fn_data); ptedata->curlen -= node->content.txtlen; /* deletes scrollview item */ if (txtdoc->del_fn) txtdoc->del_fn(txtdoc, node); textnode_destroy (node); } /* * textdoc_get_textlen : gets the total length of the text document */ static int textdoc_get_textlen (TextDoc *txtdoc) { list_t *me; TextNode *node; int total_len = 0; list_for_each (me, &txtdoc->queue) { node = list_entry (me, TextNode, list); total_len += node->content.txtlen; } return total_len; } /* * textdoc_gettext : get text string from text document */ static int textdoc_gettext (TextDoc *txtdoc, int len, unsigned char *buffer) { list_t *me; TextNode *node; unsigned char *pch = buffer; int total_len = 0, copy_len = 0; if (!buffer || len <= 0) return 0; list_for_each (me, &txtdoc->queue) { node = list_entry (me, TextNode, list); copy_len = MIN(node->content.txtlen, len - total_len); if (copy_len <= 0) break; memcpy (pch, node->content.string, copy_len); pch += copy_len; total_len += copy_len; } *pch = '\0'; return total_len; } static int textdoc_get_paragraph_length (PLOGFONT log_font, TextDoc *txtdoc, int index) { list_t *me; TextNode *node; int i = 0; int count; if (index < 0) return -1; list_for_each (me, &txtdoc->queue) { node = list_entry (me, TextNode, list); if (i++ == index) { return __mg_substrlen (log_font, node->content.string, node->content.txtlen, '\n', &count); } } return -1; } static int textdoc_get_paragraph_text (HWND hWnd, TextDoc *txtdoc, int index, int start, int len, unsigned char *buffer) { list_t *me; TextNode *node; unsigned char *pch = buffer; int total_len = 0, copy_len = 0; int count = 0, step = 0; int i = 0, last_pos = 0, fit; int *pos_chars = NULL; SIZE txtsize; HDC hdc; if (!buffer || index < -1 || start < 0 || len <= 0) return 0; list_for_each (me, &txtdoc->queue) { if (index != -1 && i++ != index) continue; node = list_entry (me, TextNode, list); if (count <= start && count + node->content.txtlen <= start) { count += node->content.txtlen; continue; } else if (count <= start && count + node->content.txtlen > start) { count += node->content.txtlen; step = start + node->content.txtlen - count; } else step = 0; if (!(pos_chars = calloc (1, sizeof(int) * (2 * node->content.txtlen + 1)))) return -1; hdc = GETDC (hWnd); GetTextExtentPoint (hdc, (const char*)node->content.string, strlen((const char*)(node->content.string)), 0, &fit, pos_chars, NULL, &txtsize); RELEASEDC (hWnd, hdc); if (step) { for (i = 0; i < fit; i++) { if (pos_chars[i] == step) break; else if (pos_chars[i] < step && ((i+1) >= fit || ((i+1) < fit && pos_chars [i+1] > step))) { step = pos_chars[i] > 0 ? pos_chars[i] : 0; break; } } } copy_len = MIN(node->content.txtlen-step, len - 1 - total_len); if (copy_len <= 0) { free (pos_chars); break; } /*It is last node copied*/ if (copy_len < node->content.txtlen-step) { last_pos = copy_len + step; /*last character process*/ if (pos_chars [fit-1] < last_pos) { if (node->content.string [node->content.txtlen] != '\n') { last_pos = node->content.txtlen; copy_len = last_pos - step; } } else { for (i = 0; i < fit; i++) { if (pos_chars[i] == last_pos) break; else if (pos_chars[i] < last_pos && (i+1) < fit && pos_chars [i+1] > last_pos) { last_pos= pos_chars[i]; copy_len = last_pos - step; } } } free (pos_chars); memcpy (pch, node->content.string + step, copy_len); pch += copy_len; total_len += copy_len; break; } free (pos_chars); memcpy (pch, node->content.string + step, copy_len); pch += copy_len; total_len += copy_len; if (index != -1) break; } *pch = '\0'; return total_len; } static int textdoc_get_num_paragraphs (TextDoc *txtdoc) { list_t *me; int total_num = 0; list_for_each (me, &txtdoc->queue) { total_num ++; } return total_num; } static char* get_limited_str(HWND hWnd, const char* str, int bytes) { static int pos, i = 0, eff_word; static int eff_chars, *pos_chars; static SIZE size; HDC hdc = GETDC (hWnd); char *dst = NULL; if ((pos_chars = malloc (bytes * sizeof(int))) == NULL) { return NULL; } eff_word = GetTextExtentPoint (hdc, str, bytes + 4, -1, &eff_chars, pos_chars, NULL, &size); if (eff_word > 0) { if ((dst = malloc (eff_chars)) == NULL) { free(pos_chars); return NULL; } memset (dst, 0, eff_chars); for (i = eff_chars - 1; i >= 0; i--) { if (pos_chars[i+1] <= bytes && pos_chars[i] < bytes ) { pos = i; memcpy(dst, str, pos_chars[i+1]); break; } } } free(pos_chars); RELEASEDC (hWnd, hdc); return dst; } /* * textdoc_settext : setting TextDoc object using a new text content and * free the old one * Params : content - new text content, if NULL, the content of the * TextDoc object will not be changed; if content * is a null string, txtdoc content will be cleared. * TODO : for not null-terminated text */ static int textdoc_settext (TextDoc *txtdoc, const char*content) { const char *pLine, *ptmp; char *str = NULL; HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); if (!txtdoc || !content) return -1; /* free the old text content */ textdoc_free (txtdoc); /*now, total length should be 0*/ ptedata->curlen = 0; if (ptedata->hardLimit != -1 && ptedata->hardLimit < strlen(content)) { str = get_limited_str(hWnd, content, ptedata->hardLimit); } else { str = (char*)content; } ptmp = pLine = str; if (str) { while (*ptmp != '\0') { if (*ptmp == txtdoc->lnsep) { /* adds a new line, including the line seperator */ txtAddNode (txtdoc, pLine, ptmp-pLine+1, NULL); pLine = ptmp + 1; } ptmp ++; } } /* adds a new blank line or the last line without a line seperator */ txtAddNode (txtdoc, pLine, ptmp-pLine, NULL); set_current_node (txtdoc, FIRSTNODE(txtdoc), FALSE, TRUE); if (ptedata->hardLimit != -1 && ptedata->hardLimit < strlen(content)) { free(str); } return 0; } static void insert_string (TextDoc *txtdoc, TextNode *curnode, int insert_pos, const char *newtext, int len) { StrBuffer *strbuff = &curnode->content; unsigned char *pLn, *pIns; HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); if (len > 0) { pLn = testr_realloc (strbuff, strbuff->txtlen + len); if (!pLn) return; pIns = pLn + insert_pos; memmove (pIns + len, pIns, strbuff->txtlen+1 - insert_pos); memcpy (pIns, newtext, len); if (ptedata->hardLimit !=-1 && ptedata->curlen + len > ptedata->hardLimit) { Ping (); NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_MAXTEXT); return; } } else { pIns = strbuff->string + insert_pos; memmove (pIns + len, pIns, strbuff->txtlen+1 - insert_pos); pLn = testr_realloc (strbuff, strbuff->txtlen + len); } ptedata->curlen += len; strbuff->txtlen += len; } #ifdef _SELECT_SUPPORT static TextMark* get_start_mark (PTEDATA ptedata) { TextDoc *txtdoc = &ptedata->txtdoc; if (ptedata->curItemY < ptedata->selItemY || (ptedata->curItemY == ptedata->selItemY && txtdoc->insert.pos_lnOff < txtdoc->selection.pos_lnOff) ) return &txtdoc->insert; else return &txtdoc->selection; } /* Get the start and end selection points in a text node */ static void get_selection_points (PTEDATA ptedata, TextNode *node, int *pos_start, int *pos_end) { TextDoc *txtdoc = &ptedata->txtdoc; TextMark *markStart = get_start_mark (ptedata); TextMark *markEnd = (markStart == &txtdoc->insert) ? &txtdoc->selection : &txtdoc->insert; if (node == txtdoc->insert.curNode || node == txtdoc->selection.curNode) { if (txtdoc->insert.curNode == txtdoc->selection.curNode) { *pos_start = markStart->pos_lnOff; *pos_end = markEnd->pos_lnOff; } else if (node == markStart->curNode) { *pos_start = markStart->pos_lnOff; *pos_end = node->content.txtlen; } else { *pos_start = 0; *pos_end = markEnd->pos_lnOff; } } else { *pos_start = 0; *pos_end = node->content.txtlen; } } /* * delete_selection : deletes the selected texts */ static int delete_selection (TextDoc *txtdoc) { int pos_start, pos_end; int pos_start2, pos_end2; TextNode *node, *startnode, *endnode; TextMark *markStart, *markEnd; HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd); markStart = get_start_mark (ptedata); markEnd = (markStart == &txtdoc->insert) ? &txtdoc->selection : &txtdoc->insert; startnode = markStart->curNode; endnode = markEnd->curNode; get_selection_points (ptedata, endnode, &pos_start, &pos_end); get_selection_points (ptedata, startnode, &pos_start2, &pos_end2); txtdoc->selection.curNode = NULL; scrollview_freeze (hWnd, &ptedata->svdata, TRUE); insert_string (txtdoc, endnode, pos_end, NULL, pos_start-pos_end); if (startnode != endnode) { while ( (node = TXTNODE_NEXT(startnode)) != endnode ) { txtDelNode (txtdoc, node); } if (pos_start2 == 0) { txtDelNode (txtdoc, startnode); startnode = NULL; txtdoc->insert.curNode = endnode; txtdoc->insert.pos_lnOff = 0; txtdoc->change_fn (txtdoc, FALSE); } else { char del[1] = {127}; textdoc_insert_string_ex (txtdoc, startnode, pos_end2-1, NULL, pos_start2-pos_end2+1); textdoc_insert_string_ex_2 (txtdoc, startnode, pos_start2, del, 1); txtdoc->insert.curNode = startnode; endnode = NULL; } } if (txtdoc->change_cont) { txtdoc->change_cont (txtdoc, endnode); txtdoc->change_cont (txtdoc, startnode); } txtdoc->selection.curNode = NULL; scrollview_unselect_all (&ptedata->svdata); scrollview_freeze (hWnd, &ptedata->svdata, FALSE); return pos_start2; } #endif static TextNode* insert_ln_sep (TextDoc *txtdoc, TextNode *curnode, int insert_pos, BOOL bChRn) { StrBuffer *strbuff = &curnode->content; TextNode *newnode; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2((HWND)txtdoc->fn_data); unsigned char *pIns; int len = bChRn ? 2 : 1; pIns = strbuff->string + insert_pos; newnode = txtAddNode ( txtdoc, (const char*)pIns, strbuff->txtlen - (pIns-strbuff->string), curnode ); strbuff->txtlen = insert_pos + len; /* add a line sep */ if (*pIns == '\0') { testr_realloc (strbuff, strbuff->txtlen); pIns = strbuff->string + insert_pos ; } if (bChRn) strncpy((char*)pIns, CH_RN, len); else *pIns = txtdoc->lnsep; *(pIns + len) = '\0'; ptedata->curlen += len; 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; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd); #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; int cplen; char *tmpstr = NULL; if (BE_FIRST_NODE(curnode)) return insert_pos; node = TXTNODE_PREV(curnode); cplen = node->content.txtlen; tmpstr = malloc(cplen + 1); if (tmpstr != NULL) { memcpy (tmpstr, node->content.string, cplen); tmpstr [cplen] = '\0'; /* deletes the previous node*/ txtDelNode (txtdoc, node); /* adds the previous line at the beginning of the current line */ insert_pos += textdoc_insert_string_ex (txtdoc, enode, insert_pos, (const char*)tmpstr, cplen-1); txtdoc->change_fn (txtdoc, FALSE); free(tmpstr); } return insert_pos; } len = - CHLENPREV((const char *)(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)) { int cplen = node->content.txtlen; char *tmpstr = NULL; /*next node have info*/ *pIns = '\0'; ptedata->curlen -= 1; curnode->content.txtlen = pIns - curnode->content.string; tmpstr = malloc(cplen + 1); if (tmpstr == NULL) break; memcpy (tmpstr, node->content.string, cplen); tmpstr[cplen]='\0'; txtDelNode (txtdoc, node); textdoc_insert_string_ex (txtdoc, enode, insert_pos, (const char*)tmpstr, cplen); insert_pos = oldpos; free(tmpstr); } else 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, (const char *)(&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) { _WRN_PRINTF ("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, GetWindowElementPixelEx (hWnd, hdc, WE_FGC_WINDOW)); 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); SetBkColor (hdc, GetWindowBkColor (hWnd)); SetTextColor (hdc, GetWindowElementPixelEx (hWnd, hdc, (GetWindowStyle(hWnd) & WS_DISABLED) ? WE_FGC_DISABLED_ITEM : WE_FGC_WINDOW)); } else { SetBkMode (hdc, BM_OPAQUE); SetTextColor (hdc, GetWindowElementPixelEx (hWnd, hdc, (GetWindowStyle(hWnd) & WS_DISABLED) ? WE_FGC_DISABLED_ITEM : WE_FGC_SELECTED_ITEM)); if (hWnd == GetFocus(GetParent(hWnd))) { SetBkColor (hdc, GetWindowElementPixelEx (hWnd, hdc, WE_BGC_SELECTED_ITEM)); } else { SetBkColor (hdc, GetWindowElementPixelEx (hWnd, hdc, WE_BGC_SELECTED_LOSTFOCUS)); } } } /* * 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); if (!node) return; content = (char*)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' && (outlen > 0 && content[outlen-1] == '\r')) outlen --; if (ptedata->lnChar) { outlen ++; chln = content[outlen-1]; content[outlen-1] = ptedata->lnChar; } } if (outlen <= 0) return; /* 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); if (!(ptedata->ex_flags & TEST_EX_SETFOCUS) && !(ptedata->ex_flags & TEST_EX_KILLFOCUS)) { NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_SELCHANGED); } } 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 ---------------- */ static int get_caret_width (HWND hWnd, PTEDATA ptedata) { if (ptedata->caretShape == ED_CARETSHAPE_LINE) { return 1; } else if (ptedata->caretShape == ED_CARETSHAPE_BLOCK) { HDC hdc = GETDC (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node = txtdoc->insert.curNode; char *pIns = ( char*)(node->content.string) + txtdoc->insert.pos_lnOff; int chlen = CHLENNEXT(pIns, node->content.txtlen - txtdoc->insert.pos_lnOff); int width = GetMaxFontWidth (hdc); if (chlen == 0) { if (ptedata->getCaretWidth) { int cw = ptedata->getCaretWidth (hWnd, width); if (cw > 0) width = cw; } } else { SIZE chsize = {0, 0}; GetTextExtent (hdc, pIns, chlen, &chsize); if (chsize.cx > 0) width = chsize.cx; } RELEASEDC (hWnd, hdc); return width; } return 1; } /* sets the current caret position in the virtual content window */ static void mySetCaretPos (HWND hWnd, int x, int y) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd); /* * no change, should return? if ( (x < 0 || x == ptedata->caret_x) && (y < 0 || y == ptedata->caret_y) ) return; */ if (x >= 0) ptedata->caret_x = x; else x = ptedata->caret_x; if (y >= 0) ptedata->caret_y = y; else y = ptedata->caret_y; scrolled_content_to_visible (ptescr, &x, &y); scrolled_visible_to_window (ptescr, &x, &y); SetCaretPos (hWnd, x, y); ChangeCaretSize (hWnd, get_caret_width(hWnd, ptedata), ptedata->nLineHeight - ptedata->nLineAboveH- ptedata->nLineBaseH); if ( (ptedata->flags & TEST_FOCUSED) && !(ptedata->flags & TEST_NOCARET) #ifdef _SELECT_SUPPORT && !(ptedata->txtdoc.selection.curNode) #endif ) { ActiveCaret (hWnd); ShowCaret (hWnd); } } /* * teSetCaretPos : sets the caret position according to the current insertion * point or sets the selection caret position according to the * current selection point of the text doc. * Description : should be called after the current insert point or the * selection point of the text document is changed */ static void teSetCaretPos (HWND hWnd, PTEDATA ptedata) { TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node; TextMark *mark; int h, indent, endh; HDC hdc; mark = GETMARK(txtdoc->selection.curNode); node = mark->curNode; if (!node) return; hdc = GETDC (hWnd); SelectFont (hdc, GetWindowFont(hWnd)); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) h = ptedata->selItemY; else #endif h = ptedata->curItemY; indent = teGetLineIndent (ptedata, node); if (BE_WRAP(hWnd)) { RECT rc; int w; SIZE txtsize = {0, 0}; int txtlen, linenr, lineidx; const unsigned char *pLine, *pIns; DTFIRSTLINE fl; rc.left = 0; rc.top = 0; rc.right = ptedata->svdata.scrdata.nContWidth; rc.bottom = GetWindowFont(hWnd)->size; linenr = NODE_LINENR(node); pLine = node->content.string; pIns = pLine + mark->pos_lnOff; txtlen = node->content.txtlen; /* if (pLine[txtlen-1] == txtdoc->lnsep) txtlen --; */ lineidx = 0; while (txtlen > 0) { DrawTextEx2 (hdc, (const char*)pLine, txtlen, &rc, indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT, &fl); if (pLine + fl.nr_chars > pIns) break; else if (pLine + fl.nr_chars == pIns) { #if 0 if (lineidx < linenr-1) { pLine += fl.nr_chars; lineidx ++; indent = 0; } #endif break; } pLine += fl.nr_chars; txtlen -= fl.nr_chars; rc.top += ptedata->nLineHeight; lineidx ++; indent = 0; } GetTabbedTextExtent(hdc, (const char*)pLine, pIns - pLine, &txtsize); w = indent + txtsize.cx; h += ptedata->nLineHeight * lineidx; ptedata->des_caret_x = w; endh = (h < ptescr->nContY) ? h : h + ptedata->nLineHeight; scrolled_make_pos_visible (hWnd, ptescr, -1, endh > 0 ? endh : 0); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) mySetSelPos (w, h + ptedata->nLineAboveH); else #endif { /* FIXME */ if ( ptedata->caretShape == ED_CARETSHAPE_BLOCK && RECTW(rc) - w <= (GetWindowFont(hWnd)->size)/2 ) { scrolled_make_pos_visible (hWnd, ptescr, -1, endh + ptedata->nLineHeight); mySetCaretPos (hWnd, indent, h + ptedata->nLineAboveH + ptedata->nLineHeight); } else { mySetCaretPos (hWnd, w, h + ptedata->nLineAboveH); } } } else { SIZE txtsize; GetTabbedTextExtent(hdc, (const char*)(node->content.string), mark->pos_lnOff, &txtsize); ptedata->des_caret_x = txtsize.cx + indent; endh = (h < ptescr->nContY) ? h : h + ptedata->nLineHeight; scrolled_make_pos_visible (hWnd, ptescr, txtsize.cx + indent, endh > 0 ? endh : 0); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) mySetSelPos (txtsize.cx + indent, h + ptedata->nLineAboveH); else #endif mySetCaretPos (hWnd, txtsize.cx + indent, h + ptedata->nLineAboveH); } RELEASEDC (hWnd, hdc); } static void textedit_set_svlist (HWND hWnd, PSCRDATA pscrdata, BOOL visChanged) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node = FIRSTNODE(txtdoc); /* reset caret pos in the real window */ if (ptedata->flags & TEST_FOCUSED) mySetCaretPos (hWnd, -1, -1); while (node) { txtdoc->change_cont (txtdoc, node); node = TXTNODE_NEXT(node); } scrollview_set_svlist (hWnd, pscrdata, visChanged); } /* * set_caret_pos : Set 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 = GETDC (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, (const char*)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; } if (pLine[ln_chars-1] == txtdoc->lnsep) { /* add caret before line seperator */ ln_chars --; if (ln_chars > 0 && is_rn_sep((pLine + ln_chars - 1)) ) ln_chars --; } if (x - indent <= 0) { out_chars = 0; txtsize.cx = 0; } else out_chars = GetTabbedTextExtentPoint (hdc, (const char*)pLine, ln_chars, x - indent, NULL, NULL, NULL, &txtsize); RELEASEDC (hWnd, hdc); if (!bSel) { txtdoc->insert.pos_lnOff = pLine - string + out_chars; mySetCaretPos (hWnd, txtsize.cx + indent, h + ptedata->nLineAboveH); } #ifdef _SELECT_SUPPORT else { txtdoc->selection.pos_lnOff = pLine - string + out_chars; mySetSelPos (txtsize.cx + indent, h + ptedata->nLineAboveH); } #endif return 0; } /* calculates line number of a text node, for wrap mode */ static int get_line_nr (HWND hWnd, PTEDATA ptedata, TextNode *node, int indent) { RECT rc; PLOGFONT logfont = GetWindowFont(hWnd); HDC hdc; if (!node) { _WRN_PRINTF ("pass NULL text node to get_line_nr\n"); return 0; } if (!BE_WRAP(hWnd)) return 1; hdc = GETDC (hWnd); SelectFont (hdc, logfont); rc.left = 0; rc.top = 0; rc.right = scrolled_get_contwidth(ptescr); rc.bottom = logfont->size; if (ptedata->lnChar) { char *content, chln = 0; int outlen; content = (char*)node->content.string; outlen = node->content.txtlen; if (outlen > 0 && content[outlen-1] == ptedata->txtdoc.lnsep) { outlen --; if (ptedata->txtdoc.lnsep == '\n' && content[outlen-1] == '\r') outlen --; outlen ++; chln = content[outlen-1]; content[outlen-1] = ptedata->lnChar; } DrawTextEx (hdc, content, outlen, &rc, indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT); if (chln) content[outlen-1] = chln; } else { DrawTextEx (hdc, (const char*)(node->content.string), node->content.txtlen, &rc, indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT); } RELEASEDC (hWnd, hdc); return (RECTH(rc)/logfont->size); } /* * TODO */ static int recalc_max_len (HWND hWnd ,PTEDATA ptedata) { list_t *me; TextDoc *txtdoc = &ptedata->txtdoc; SIZE txtsize; TextNode *node; HDC hdc; ptedata->maxLen = 0; ptedata->maxNode = NULL; ptedata->secLen = 0; ptedata->secNode = NULL; hdc = GETDC (hWnd); //SelectFont (hdc, GetWindowFont(hWnd)); list_for_each (me, &txtdoc->queue) { node = list_entry (me, TextNode, list); GetTabbedTextExtent(hdc, (const char*)(node->content.string), node->content.txtlen, &txtsize); if (txtsize.cx > ptedata->maxLen || ptedata->maxLen == 0) { ptedata->maxLen = txtsize.cx; ptedata->maxNode = node; } else if (txtsize.cx >= ptedata->secLen) { ptedata->secLen = txtsize.cx; ptedata->secNode = node; } } RELEASEDC (hWnd, hdc); return 0; } static int revise_max_len (HWND hWnd, PTEDATA ptedata, TextNode *node, int textlen) { if (textlen > ptedata->maxLen) { if (node != ptedata->maxNode) { ptedata->secLen = ptedata->maxLen; ptedata->secNode = ptedata->maxNode; } ptedata->maxLen = textlen; ptedata->maxNode = node; } else if (textlen > ptedata->secLen) { if (node != ptedata->maxNode) { ptedata->secLen = textlen; ptedata->secNode = node; return 0; /* no need to reset content width */ } else { ptedata->maxLen = textlen; } } else { /* FIXME */ if (node == ptedata->maxNode) { recalc_max_len (hWnd, ptedata); return 1; } else if (node == ptedata->secNode) { recalc_max_len (hWnd, ptedata); return 0; } return 0; } /* need to reset content width */ return 1; } /* set text node line width, for non wrap mode */ static int set_line_width (HWND hWnd, PTEDATA ptedata, TextNode *node, int indent) { SIZE txtsize = {0, 0}; HDC hdc; if (!node) { _WRN_PRINTF ("pass NULL text node to set_line_width\n"); return -1; } if (BE_WRAP(hWnd)) return -1; hdc = GETDC (hWnd); SelectFont (hdc, GetWindowFont(hWnd)); GetTabbedTextExtent(hdc, (const char*)(node->content.string), node->content.txtlen, &txtsize); RELEASEDC (hWnd, hdc); if (revise_max_len (hWnd, ptedata, node, txtsize.cx + indent) > 0) scrolled_set_cont_width (hWnd, ptescr, ptedata->maxLen + 1); return 0; } /* static int set_line_size (HWND hWnd, PTEDATA ptedata, TextNode *node) { int linenr = 1, indent; if (!node) return 0; indent = teGetLineIndent (ptedata, node); linenr = get_line_nr(hWnd, ptedata, node, indent); set_line_width (hWnd, ptedata, node, indent); return scrollview_set_item_height (hWnd, (HSVITEM)node->addData, ptedata->nLineHeight*linenr); } */ /* * get_node_by_idx : Get node and y position by node index */ static TextNode* get_node_by_idx (PTEDATA ptedata, int line, int *item_y) { TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node; int nr = 0, ypos = 0; if (line < 0) return NULL; node = FIRSTNODE(txtdoc); while (node) { if (nr == line) break; nr ++; ypos += NODE_HEIGHT(node); node = TXTNODE_NEXT(node); } if (nr != line) return NULL; if (item_y) *item_y = ypos; return node; } static void teNodeInit (TextDoc *txtdoc, TextNode* node, TextNode *prenode) { SVITEMINFO svii; HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); int linenr = 1, indent; HSVITEM preitem = 0; if (!node) return; indent = teGetLineIndent (ptedata, node); linenr = get_line_nr(hWnd, ptedata, node, indent); set_line_width (hWnd, ptedata, node, indent); svii.nItemHeight = ptedata->nLineHeight * linenr; svii.addData = (DWORD)node; svii.nItem = -1; if (prenode) preitem = (HSVITEM)prenode->addData; node->addData = (DWORD) scrollview_add_item (hWnd, &ptedata->svdata, preitem, &svii, NULL); } static void teNodeDel (TextDoc *txtdoc, TextNode *node) { HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); HSVITEM hsvi = (HSVITEM) node->addData; node->addData = 0; scrollview_del_item (hWnd, &ptedata->svdata, 0, hsvi); } static void teNodeChange (TextDoc *txtdoc, BOOL bSel) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2((HWND)txtdoc->fn_data); if (!bSel) { ptedata->curItemY = scrollview_get_item_ypos (&ptedata->svdata, (HSVITEM)txtdoc->insert.curNode->addData); } #ifdef _SELECT_SUPPORT else { ptedata->selItemY = scrollview_get_item_ypos (&ptedata->svdata, (HSVITEM)txtdoc->selection.curNode->addData); } #endif } static void teChangeCont (TextDoc *txtdoc, TextNode *node) { HWND hWnd = (HWND)txtdoc->fn_data; PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); int indent, linenr; if (!node) return; indent = teGetLineIndent (ptedata, node); linenr = get_line_nr(hWnd, ptedata, node, indent); scrollview_set_item_height (hWnd, (HSVITEM)node->addData, ptedata->nLineHeight*linenr); set_line_width (hWnd, ptedata, node, indent); } /* * textdoc_reset : resets or initializes the properties of a textdoc to * default values */ static int textdoc_reset (HWND hWnd, TextDoc *txtdoc) { txtdoc->lnsep = DEF_LINESEP; txtdoc->nDefLineSize = DEF_LINE_BUFFER_SIZE; txtdoc->nBlockSize = DEF_LINE_BLOCK_SIZE; txtdoc->init_fn = teNodeInit; txtdoc->change_fn = teNodeChange; txtdoc->change_cont = teChangeCont; txtdoc->del_fn = teNodeDel; txtdoc->fn_data = (void*)hWnd; return 0; } /* * teResetData : resets status data */ static void teResetData (PTEDATA ptedata) { ptedata->des_caret_x = 0; ptedata->curItemY = 0; #ifdef _SELECT_SUPPORT ptedata->selItemY = 0; #endif ptedata->maxNode = NULL; ptedata->maxLen = 0; ptedata->secNode = NULL; ptedata->secLen = 0; ptedata->flags = 0; } /* -------------------------------------------------------------------------- */ #if defined(_MGHAVE_CLIPBOARD) && defined(_SELECT_SUPPORT) int textdoc_copy_to_cb (PTEDATA ptedata, char *buffer, int len, BOOL bCB) { TextDoc *txtdoc = &ptedata->txtdoc; int pos_start, pos_end; TextNode *node, *endnode; TextMark *markStart, *markEnd; int org_len = len; if (!txtdoc->selection.curNode) return 0; markStart = get_start_mark (ptedata); markEnd = (markStart == &txtdoc->insert) ? &txtdoc->selection : &txtdoc->insert; node = markStart->curNode; endnode = markEnd->curNode; /* clear clipboard */ if (bCB) SetClipBoardData (CBNAME_TEXT, NULL, 0, CBOP_NORMAL); while (1) { get_selection_points (ptedata, node, &pos_start, &pos_end); if (bCB) { SetClipBoardData (CBNAME_TEXT, node->content.string + pos_start, pos_end - pos_start, CBOP_APPEND); } else if (buffer) { int output; output = MIN(len, pos_end - pos_start); strncpy (buffer, (const char*)(node->content.string) + pos_start, output); buffer += output; len -= output; } else { len += pos_end - pos_start; } if(node != endnode) node = TXTNODE_NEXT (node); else break; } if (!bCB && !buffer && org_len == 0) return len; return org_len - len; } #endif static void te_make_caret_visible (HWND hWnd, PTEDATA ptedata, BOOL bSel) { POINT pt; if (!bSel) myGetCaretPos (&pt); #ifdef _SELECT_SUPPORT else myGetSelPos (&pt); #endif if (pt.y > ptescr->nContY) pt.y = (pt.y / ptedata->nLineHeight + 1) * ptedata->nLineHeight; else pt.y = (pt.y / ptedata->nLineHeight) * ptedata->nLineHeight; scrolled_make_pos_visible (hWnd, ptescr, pt.x, pt.y); if (!bSel) { mySetCaretPos (hWnd, -1, -1); /* * FIXME * ShowCaret (hWnd); */ } } static void teSetCaretPosOnChar (HWND hWnd, PTEDATA ptedata, int old_caret_pos) { TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node; TextMark *mark; int h, indent, endh; HDC hdc; /*initialize line info*/ ptedata->nr_diff_line = -1; ptedata->w_nochanged = 0; ptedata->w_lastline = 0; mark = GETMARK(txtdoc->selection.curNode); node = mark->curNode; if (!node) return; hdc = GETDC (hWnd); SelectFont (hdc, GetWindowFont(hWnd)); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) h = ptedata->selItemY; else #endif h = ptedata->curItemY; indent = teGetLineIndent (ptedata, node); old_caret_pos = MIN(old_caret_pos, mark->pos_lnOff); if (BE_WRAP(hWnd)) { RECT rc; int w; SIZE txtsize = {0, 0}; int oldtxtlen, newtxtlen, linenr, lineidx; const unsigned char *newLine, *oldLine, *pIns, *oldIns; DTFIRSTLINE oldfl, newfl; rc.left = 0; rc.top = 0; rc.right = ptedata->svdata.scrdata.nContWidth; rc.bottom = GetWindowFont(hWnd)->size; oldLine = ptedata->tebuff.string; oldtxtlen = ptedata->tebuff.txtlen; linenr = NODE_LINENR(node); newLine = node->content.string; newtxtlen = node->content.txtlen; pIns = newLine + mark->pos_lnOff; oldIns = oldLine + old_caret_pos; lineidx = 0; newfl.width = 0; while (old_caret_pos > 0) { DrawTextEx2 (hdc, (const char*)oldLine, oldtxtlen, &rc, lineidx ? 0: indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT, &oldfl); DrawTextEx2 (hdc, (const char*)newLine, newtxtlen, &rc, lineidx ? 0 : indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT, &newfl); /*compare two string */ if (oldfl.nr_chars != newfl.nr_chars) { ptedata->nr_diff_line = lineidx; if (oldfl.nr_chars < newfl.nr_chars) ptedata->w_nochanged = oldfl.width; else ptedata->w_nochanged = newfl.width; } if (oldLine + oldfl.nr_chars > oldIns) { ptedata->nr_diff_line = lineidx; GetTabbedTextExtent(hdc, (const char*)oldLine, oldIns - oldLine, &txtsize); if (txtsize.cx < ptedata->w_nochanged) ptedata->w_nochanged = txtsize.cx; } if (ptedata->nr_diff_line != -1) break; newLine += newfl.nr_chars; oldLine += oldfl.nr_chars; oldtxtlen -= oldfl.nr_chars; newtxtlen -= newfl.nr_chars; lineidx ++; old_caret_pos -= oldfl.nr_chars; } if (ptedata->nr_diff_line == -1) { ptedata->nr_diff_line = lineidx; GetTabbedTextExtent(hdc, (const char*)oldLine, old_caret_pos, &txtsize); ptedata->w_nochanged = txtsize.cx; } if (lineidx == 0) ptedata->w_nochanged += indent; newLine = node->content.string; newtxtlen = node->content.txtlen; lineidx = 0; while (newtxtlen > 0) { DrawTextEx2 (hdc, (const char*)newLine, newtxtlen, &rc, indent, TE_FORMAT | DT_WORDBREAK | DT_CALCRECT, &newfl); if (newLine + newfl.nr_chars > pIns) break; else if (newLine + newfl.nr_chars == pIns) { #if 0 if (lineidx < linenr-1) { newLine += newfl.nr_chars; lineidx ++; indent = 0; } #endif break; } newLine += newfl.nr_chars; newtxtlen -= newfl.nr_chars; rc.top += ptedata->nLineHeight; lineidx ++; indent = 0; } GetTabbedTextExtent(hdc, (const char*)newLine, pIns - newLine, &txtsize); w = indent + txtsize.cx; h += ptedata->nLineHeight * lineidx; ptedata->des_caret_x = w; endh = (h < ptescr->nContY) ? h : h + ptedata->nLineHeight; scrolled_make_pos_visible (hWnd, ptescr, -1, endh > 0 ? endh : 0); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) { mySetSelPos (w, h + ptedata->nLineAboveH); } else #endif { /* FIXME */ if ( ptedata->caretShape == ED_CARETSHAPE_BLOCK && RECTW(rc) - w <= (GetWindowFont(hWnd)->size)/2 ) { scrolled_make_pos_visible (hWnd, ptescr, -1, endh + ptedata->nLineHeight); mySetCaretPos (hWnd, indent, h + ptedata->nLineAboveH + ptedata->nLineHeight); } else { mySetCaretPos (hWnd, w, h + ptedata->nLineAboveH); } } } else { SIZE txtsize; ptedata->nr_diff_line = 0; GetTabbedTextExtent(hdc, (const char*)(node->content.string), old_caret_pos, &txtsize); ptedata->w_nochanged = indent + txtsize.cx; GetTabbedTextExtent(hdc, (const char*)(node->content.string), mark->pos_lnOff, &txtsize); ptedata->des_caret_x = txtsize.cx + indent; endh = (h < ptescr->nContY) ? h : h + ptedata->nLineHeight; scrolled_make_pos_visible (hWnd, ptescr, txtsize.cx + indent, endh > 0 ? endh : 0); #ifdef _SELECT_SUPPORT if (txtdoc->selection.curNode) { mySetSelPos (txtsize.cx + indent, h + ptedata->nLineAboveH); } else #endif mySetCaretPos (hWnd, txtsize.cx + indent, h + ptedata->nLineAboveH); } RELEASEDC (hWnd, hdc); } static void teOnChar (HWND hWnd, PTEDATA ptedata, WPARAM wParam) { int chlen; unsigned char ch[4]; TextNode *node; TextMark *mark; TextDoc *txtdoc; DWORD dwStyle = GetWindowStyle(hWnd); int old_caret_pos; RECT rcInv; int oldlinenr, newlinenr, i; if (dwStyle & ES_READONLY) { Ping(); return; } ch [0] = FIRSTBYTE (wParam); ch [1] = SECONDBYTE (wParam); ch [2] = THIRDBYTE (wParam); ch [3] = FOURTHBYTE (wParam); if (ch[3]) { chlen = 4; } else if (ch[2]) { chlen = 3; } else if (ch[1]) { chlen = 2; } else { chlen = 1; if ( ch[0] < 0x20 && ch[0] != '\b' && ch[0] != '\t' && ch[0] != '\n' && ch[0] != '\r' ) { return; } if (dwStyle & ES_UPPERCASE) { ch [0] = toupper (ch[0]); } else if (dwStyle & ES_LOWERCASE) { ch [0] = tolower (ch[0]); } /* ENTER */ if (ch [0] == '\r') { ch [0] = ptedata->txtdoc.lnsep; } } node = ptedata->txtdoc.insert.curNode; txtdoc = &(ptedata->txtdoc); mark = GETMARK(txtdoc->selection.curNode); old_caret_pos = mark->pos_lnOff; oldlinenr = NODE_LINENR(node); testr_copy(&(ptedata->tebuff), &(node->content)); if (ptedata->hardLimit == -1 || (ptedata->curlen + chlen <= ptedata->hardLimit) || (chlen == 1 && (ch[0] == '\b' || ch[0] == 127))) { if (textdoc_insert_string (&ptedata->txtdoc, (const char*)ch, chlen)) { teSetCaretPosOnChar (hWnd, ptedata, old_caret_pos); rcInv.left = ptedata->w_nochanged; rcInv.top = ptedata->nr_diff_line * ptedata->nLineHeight; rcInv.right = ptedata->svdata.scrdata.nContWidth; rcInv.bottom = rcInv.top + ptedata->nLineHeight - ptedata->nLineBaseH; REFRESH_NODE_EX(node, &rcInv); newlinenr = NODE_LINENR(node); rcInv.left = 0; for (i = ptedata->nr_diff_line + 1; i < MAX(oldlinenr, newlinenr); i++) { rcInv.top += ptedata->nLineHeight; rcInv.bottom = rcInv.top + ptedata->nLineHeight - ptedata->nLineBaseH; REFRESH_NODE_EX(node, &rcInv); } /* houhh 20090814, UpdataWindow will invalidate all edit area, but * erase area is not same with msg_paint area.*/ UpdateInvalidClient(hWnd, TRUE); } } else { Ping (); NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_MAXTEXT); } if (node != ptedata->txtdoc.insert.curNode) { node = ptedata->txtdoc.insert.curNode; /* FIXME, optimize */ teNodeChange (&ptedata->txtdoc, FALSE); teSetCaretPos (hWnd, ptedata); REFRESH_NODE(node); } #ifdef _SELECT_SUPPORT teSetCaretPos (hWnd, ptedata); #endif NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CHANGE); ptedata->cont_dirty = TRUE; } static void teOnMouseDown (HWND hWnd, TextNode *node, POINT *pt, int item_y, BOOL bShift) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); TextDoc *txtdoc = &ptedata->txtdoc; if (!node) return; if (!bShift) { txtdoc->insert.curNode = node; ptedata->curItemY = item_y; } #ifdef _SELECT_SUPPORT else { txtdoc->selection.curNode = node; ptedata->selItemY = item_y; } #endif set_caret_pos (hWnd, ptedata, node, pt->x, pt->y, bShift); } #ifdef _SELECT_SUPPORT static void get_selected_rect (PTEDATA ptedata, RECT *rcSel) { TextDoc *txtdoc = &ptedata->txtdoc; rcSel->left = 0; rcSel->right = 0; if (ptedata->curItemY < ptedata->selItemY) { rcSel->top = ptedata->curItemY; rcSel->bottom = ptedata->selItemY + NODE_HEIGHT(txtdoc->selection.curNode); } else { rcSel->top = ptedata->selItemY; rcSel->bottom = ptedata->curItemY + NODE_HEIGHT(txtdoc->insert.curNode); } } /* * te_unselect_all : unselects the selected parts * Params : bChange - whether to change the insertion point to the * position of the selection point */ static void te_unselect_all (HWND hWnd, PTEDATA ptedata, BOOL bChange) { TextDoc *txtdoc = &ptedata->txtdoc; RECT rcSel; POINT pt; pt.x = 0; pt.y = 0; if (txtdoc->selection.curNode) { get_selected_rect (ptedata, &rcSel); if (bChange) { txtdoc->insert = txtdoc->selection; ptedata->curItemY = ptedata->selItemY; myGetSelPos (&pt); } txtdoc->selection.curNode = NULL; scrollview_unselect_all (&ptedata->svdata); scrolled_refresh_rect (ptescr, &rcSel); if (bChange) { mySetCaretPos (hWnd, pt.x, pt.y); ptedata->des_caret_x = pt.x; } } } static int te_set_selection (HWND hWnd, PTEDATA ptedata) { TextDoc *txtdoc = &ptedata->txtdoc; TextMark *markStart, *markEnd; TextNode *node; RECT rcSel; markStart = get_start_mark (ptedata); markEnd = (markStart == &txtdoc->insert) ? &txtdoc->selection : &txtdoc->insert; node = markStart->curNode; while (node) { SELECT_NODE (node, TRUE); if (node == markEnd->curNode) break; node = TXTNODE_NEXT (node); } get_selected_rect (ptedata, &rcSel); scrolled_refresh_rect (ptescr, &rcSel); check_caret (); return 0; } static int te_select_all (HWND hWnd, PTEDATA ptedata) { TextMark *selection = &ptedata->txtdoc.selection; TextMark *insertion = &ptedata->txtdoc.insert; TextNode *last; if (selection->curNode) { te_unselect_all (hWnd, ptedata, TRUE); } insertion->pos_lnOff = 0; insertion->curNode = FIRSTNODE(&ptedata->txtdoc); ptedata->curItemY = 0; teSetCaretPos (hWnd, ptedata); last = LASTNODE(&ptedata->txtdoc); selection->curNode = last; selection->pos_lnOff = last->content.txtlen; ptedata->selItemY = scrollview_get_total_item_height (&ptedata->svdata) - NODE_HEIGHT(last); /* FIXME, optimize */ teSetCaretPos (hWnd, ptedata); return te_set_selection (hWnd, ptedata); } #endif /* * te_set_position : Set insertion or selection position * Params : mark - insertion or selection point */ static TextNode* te_set_position (HWND hWnd, PTEDATA ptedata, TextMark *mark, int line, int char_pos, int *item_y, int *newpos) { TextNode *markNode; int *pos_chars; int nr_chars; if (char_pos < 0) return NULL; if ( !(markNode = get_node_by_idx (ptedata, line, item_y)) ) return NULL; if (char_pos > markNode->content.txtlen) return NULL; pos_chars = ALLOCATE_LOCAL (markNode->content.txtlen * sizeof(int)); nr_chars = GetTextMCharInfo (GetWindowFont (hWnd), (const char*)(markNode->content.string), markNode->content.txtlen, pos_chars); if (char_pos > nr_chars) { DEALLOCATE_LOCAL (pos_chars); return NULL; } if (char_pos == nr_chars) char_pos = markNode->content.txtlen; else char_pos = pos_chars[char_pos]; DEALLOCATE_LOCAL (pos_chars); if (newpos) *newpos = char_pos; return markNode; } static int TE_OnMouseDown (HWND hWnd, HSVITEM hsvi, POINT* pt, int item_y, BOOL bShift) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2(hWnd); TextNode *node = NULL; #ifdef _SELECT_SUPPORT TextDoc *txtdoc = &ptedata->txtdoc; TextMark *selection = &txtdoc->selection; #endif /* note that hsvi may be null */ if (hsvi) node = (TextNode*)scrollview_get_item_adddata (hsvi); #ifdef _SELECT_SUPPORT if (selection->curNode) { te_unselect_all (hWnd, ptedata, !bShift); } #endif if (!bShift) { if (node) ptedata->des_caret_x = pt->x; teOnMouseDown (hWnd, node, pt, item_y, bShift); /* * FIXME * ShowCaret (hWnd); */ /* NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CLICKED); */ /* * FIXME * NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CHANGE); */ } #ifdef _SELECT_SUPPORT else { teOnMouseDown (hWnd, node, pt, item_y, bShift); te_set_selection (hWnd, ptedata); } #endif return 0; } #ifdef _SELECT_SUPPORT static void begin_selection (PTEDATA ptedata) { ptedata->txtdoc.selection = ptedata->txtdoc.insert; ptedata->selItemY = ptedata->curItemY; ptedata->sel_x = ptedata->caret_x; ptedata->sel_y = ptedata->caret_y; SELECT_NODE(ptedata->txtdoc.selection.curNode, TRUE); } #endif static void te_cursor_updown (HWND hWnd, PTEDATA ptedata, int len, BOOL bShift) { TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node; TextMark *mark; int item_y; POINT pt; #ifdef _SELECT_SUPPORT TextNode *org_node; if (!bShift && txtdoc->selection.curNode) { te_unselect_all (hWnd, ptedata, TRUE); } mark = bShift ? &txtdoc->selection : &txtdoc->insert; if (bShift && !mark->curNode) { begin_selection (ptedata); } node = mark->curNode; org_node = mark->curNode; item_y = bShift ? ptedata->selItemY : ptedata->curItemY; #else mark = &txtdoc->insert; node = mark->curNode; item_y = ptedata->curItemY; #endif #ifdef _SELECT_SUPPORT if (bShift) { myGetSelPos (&pt); } else #endif myGetCaretPos (&pt); pt.y += len * ptedata->nLineHeight; pt.x = ptedata->des_caret_x; if (len == 1) { /* go to next line */ if (pt.y >= item_y + NODE_HEIGHT(node)) { /* go to next node */ if (BE_LAST_NODE(node)) return; item_y += NODE_HEIGHT(node); node = TXTNODE_NEXT(node); } } else if (len == -1) { if (pt.y < item_y) { /* go to previous node */ if (BE_FIRST_NODE(node)) return; node = TXTNODE_PREV(node); item_y -= NODE_HEIGHT(node); } } else { /* TODO */ return; } pt.y -= item_y; teOnMouseDown (hWnd, node, &pt, item_y, bShift); te_make_caret_visible (hWnd, ptedata, bShift); #ifdef _SELECT_SUPPORT if (bShift) { if(NODE_IS_SELECTED(node)) { if (node != org_node) SELECT_NODE(org_node, FALSE); } else { SELECT_NODE(node, TRUE); } if (node != org_node) REFRESH_NODE(org_node); REFRESH_NODE(node); check_caret(); } #endif } /* * te_cursor_shift : process shift keys, including left and right arrow, Home, * End keys. * Params : bShift - Whether shift key is in pressed status */ static void te_cursor_shift (HWND hWnd, PTEDATA ptedata, int kcode, BOOL bShift) { TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node, *curnode; unsigned char *pIns; int len; POINT pt; TextMark *mark; #ifdef _SELECT_SUPPORT /* unselect the selected first */ if (!bShift && txtdoc->selection.curNode) { te_unselect_all (hWnd, ptedata, TRUE); } mark = bShift ? &txtdoc->selection : &txtdoc->insert; if (bShift && !mark->curNode) { begin_selection (ptedata); } #else mark = &txtdoc->insert; #endif node = mark->curNode; pIns = node->content.string + mark->pos_lnOff; if (kcode == SCANCODE_CURSORBLOCKRIGHT) { if (*pIns == '\0') return; else if (*pIns == txtdoc->lnsep || is_rn_sep(pIns)) { curnode = TXTNODE_NEXT(node); set_current_node (txtdoc, curnode, bShift, FALSE); #ifdef _SELECT_SUPPORT if (bShift) { SELECT_NODE(node, (NODE_IS_SELECTED(curnode) ? FALSE : TRUE) ); SELECT_NODE(curnode, TRUE); ptedata->selItemY += NODE_HEIGHT(node); } else #endif ptedata->curItemY += NODE_HEIGHT(node); } else { mark->pos_lnOff += CHLENNEXT(pIns, (node->content.string + node->content.txtlen - pIns)); } } else if (kcode == SCANCODE_CURSORBLOCKLEFT) { len = -1; if (pIns == node->content.string) { if (BE_FIRST_NODE(node)) return; else { curnode = TXTNODE_PREV(node); set_current_node (txtdoc, curnode, bShift, FALSE); #ifdef _SELECT_SUPPORT if (bShift) { SELECT_NODE(node, (NODE_IS_SELECTED(curnode) ? FALSE : TRUE) ); SELECT_NODE(curnode, TRUE); ptedata->selItemY -= NODE_HEIGHT(TXTNODE_PREV(node)); } else #endif ptedata->curItemY -= NODE_HEIGHT(TXTNODE_PREV(node)); mark->pos_lnOff = TXTNODE_PREV(node)->content.txtlen - 1; } } else { mark->pos_lnOff -= CHLENPREV((const char *)(node->content.string), pIns); } } else if (kcode == SCANCODE_HOME || kcode == SCANCODE_END) { myGetCaretPos (&pt); pt.y -= ptedata->curItemY; pt.x = (kcode == SCANCODE_HOME)? 0 : scrolled_get_contwidth (ptescr) - 1; if (node) ptedata->des_caret_x = pt.x; teOnMouseDown (hWnd, node, &pt, ptedata->curItemY, bShift); te_make_caret_visible (hWnd, ptedata, bShift); goto SFRETURN; } teSetCaretPos (hWnd, ptedata); SFRETURN: #ifdef _SELECT_SUPPORT if (bShift) { REFRESH_NODE(mark->curNode); check_caret (); } #endif } #ifdef _SELECT_SUPPORT static void te_cursor_move (HWND hWnd, PTEDATA ptedata, POINT *pt) { int item_h; TextDoc *txtdoc = &ptedata->txtdoc; TextMark *selection = &txtdoc->selection; TextNode *curnode; POINT oldpt, newpt; RECT rcInv; /* begin to select */ if (!selection->curNode) { begin_selection (ptedata); return; } curnode = selection->curNode; item_h = scrollview_get_item_height (curnode->addData); if (pt->y >= ptedata->selItemY + item_h) { TextNode *next = TXTNODE_NEXT (curnode); if (next) { selection->curNode = next; ptedata->selItemY += item_h; } else { if (selection->pos_lnOff != curnode->content.txtlen) { selection->pos_lnOff = curnode->content.txtlen; REFRESH_NODE(curnode); check_caret (); } return; } /* FIXME */ pt->y = ptedata->selItemY + item_h + ptedata->nLineHeight/2; } else if (pt->y < ptedata->selItemY) { TextNode *prev = TXTNODE_PREV (curnode); if (prev) { selection->curNode = prev; item_h = scrollview_get_item_height ((HSVITEM)prev->addData); ptedata->selItemY -= item_h; } else { if (selection->pos_lnOff != 0) { selection->pos_lnOff = 0; REFRESH_NODE(curnode); check_caret (); } return; } pt->y = ptedata->selItemY - ptedata->nLineHeight/2; } if (ptedata->flags & TEST_MOVE) { /* int step; step = pt->y - ptescr->nContY - ptescr->visibleHeight; step = step > ptedata->nLineHeight ? 6 : 3; */ scrolled_make_pos_visible (hWnd, ptescr, pt->x, pt->y); } pt->y -= ptedata->selItemY; /* moves to a new line */ if (curnode && selection->curNode != curnode) { /* moves off the current selected line */ if (NODE_IS_SELECTED(selection->curNode)) SELECT_NODE(curnode, FALSE); else SELECT_NODE(selection->curNode, TRUE); REFRESH_NODE(curnode); } myGetSelPos (&oldpt); set_caret_pos (hWnd, ptedata, selection->curNode, pt->x, pt->y, TRUE); myGetSelPos (&newpt); #if 0 if (oldpt.x != newpt.x || oldpt.y != newpt.y) REFRESH_NODE(selection->curNode); #else /* for bug 2763 (wangjian 2008-05-20) */ /* for bug 3104, houhh 20081029, rcInv is relative to current item rect.*/ if (oldpt.x != newpt.x && oldpt.y == newpt.y) { rcInv.left = newpt.x > oldpt.x ? oldpt.x : newpt.x; rcInv.top = 0; rcInv.right = oldpt.x > newpt.x ? oldpt.x : newpt.x; rcInv.bottom = rcInv.top + ptedata->nLineHeight - ptedata->nLineBaseH; REFRESH_NODE_EX(selection->curNode, &rcInv); }else if (oldpt.y != newpt.y) { rcInv.left = 0; rcInv.top = 0; rcInv.right = rcInv.left + ptedata->svdata.scrdata.nContWidth; //rcInv.bottom = (newpt.y > oldpt.y ? newpt.y : oldpt.y) rcInv.bottom = abs(newpt.y - oldpt.y) + ptedata->nLineHeight - ptedata->nLineBaseH; REFRESH_NODE_EX(selection->curNode, &rcInv); } #endif check_caret (); } #endif static SVITEMOPS textedit_iops = { NULL, NULL, teDrawItem }; void textedit_set_draw_selected (HWND hWnd, ED_DRAWSEL_FUNC drawSel) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); ptedata->drawSelected = drawSel; InvalidateRect (hWnd, NULL, TRUE); } void textedit_set_caret_func (HWND hWnd, int (*getcaretwidth) (HWND, int) ) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); ptedata->getCaretWidth = getcaretwidth; } void textedit_refresh_caret (HWND hWnd) { mySetCaretPos (hWnd, -1, -1); } /* --------------------------------------------------------------------------------- */ static int textedit_get_pos_ex (HWND hWnd, int *line_pos, int *char_pos, BOOL bSel) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; int nr_chars = 0; TextMark *mark; mark = GETMARK(bSel); if (!mark->curNode) return -1; if (line_pos) { *line_pos = NODE_INDEX(mark->curNode); } if (char_pos) { nr_chars = GetTextMCharInfo (GetWindowFont (hWnd), (const char*)mark->curNode->content.string, mark->pos_lnOff, NULL); *char_pos = nr_chars; } return mark->pos_lnOff; } int textedit_get_caretpos (HWND hWnd, int *line_pos, int *char_pos) { return textedit_get_pos_ex (hWnd, line_pos, char_pos, FALSE); } int textedit_get_selpos (HWND hWnd, int *line_pos, int *char_pos) { return textedit_get_pos_ex (hWnd, line_pos, char_pos, TRUE); } static int textedit_set_pos_ex (HWND hWnd, int line_pos, int char_pos, BOOL bSel) { PTEDATA ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; int item_y, newpos; TextMark *mark; TextNode *node; if ( !(mark = GETMARK(bSel)) ) return -1; if ( !(node = te_set_position (hWnd, ptedata, mark, line_pos, char_pos, &item_y, &newpos)) ) return -1; #ifdef _SELECT_SUPPORT /* unselect the former seleted before mark is changed */ if (txtdoc->selection.curNode) { te_unselect_all (hWnd, ptedata, TRUE); } #endif SETITEMY(bSel, item_y); mark->pos_lnOff = newpos; mark->curNode = node; #ifdef _SELECT_SUPPORT if (bSel) te_set_selection (hWnd, ptedata); else #endif teSetCaretPos (hWnd, ptedata); return mark->pos_lnOff; } #ifdef _TITLE_SUPPORT static int textedit_get_titletext (HWND hWnd, PTEDATA ptedata, int tlen, char *buffer) { int len, title_len; if (!ptedata->title) return -1; title_len = strlen (ptedata->title); if (!buffer) return title_len; if (tlen >= 0) len = (tlen > DEF_TITLE_LEN) ? DEF_TITLE_LEN : tlen; else len = DEF_TITLE_LEN; strncpy (buffer, ptedata->title, len); buffer[len] = '\0'; return title_len; } static int textedit_set_titletext (HWND hWnd, PTEDATA ptedata, int tlen, const char *newtitle) { int len; HDC hdc; if (!ptedata->title || !newtitle) return -1; if (tlen >= 0) len = (tlen > DEF_TITLE_LEN) ? DEF_TITLE_LEN : tlen; else len = DEF_TITLE_LEN; strncpy (ptedata->title, newtitle, len); ptedata->title[len] = '\0'; hdc = GETDC (hWnd); SelectFont (hdc, GetWindowFont(hWnd)); ptedata->titleIndent = teGetTitleIndent (hWnd, ptedata, hdc); RELEASEDC (hWnd, hdc); mySetCaretPos (hWnd, ptedata->titleIndent, 0 + ptedata->nLineAboveH); return strlen (ptedata->title); } #endif static void textedit_reset_content (HWND hWnd, PTEDATA ptedata, const char *newtext, BOOL bLast) { TextNode *node=NULL; TextDoc *txtdoc = &ptedata->txtdoc; node = FIRSTNODE(&ptedata->txtdoc); if (node) node->addData = 0; scrollview_reset_content (hWnd, &ptedata->svdata); teResetData (ptedata); scrollview_freeze (hWnd, &ptedata->svdata, TRUE); textdoc_settext (txtdoc, newtext); scrollview_freeze (hWnd, &ptedata->svdata, FALSE); if ( bLast && !(GetWindowStyle(hWnd) & ES_READONLY) ) { TextNode *node = LASTNODE(txtdoc); txtdoc->insert.curNode = node; txtdoc->insert.pos_lnOff = node->content.txtlen; teNodeChange (txtdoc, FALSE); teSetCaretPos (hWnd, ptedata); } else { mySetCaretPos (hWnd, teGetLineIndent(ptedata, FIRSTNODE(txtdoc)), 0 + ptedata->nLineAboveH); } NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_UPDATE); ptedata->cont_dirty = TRUE; } int textedit_insert_text (HWND hWnd, const char* text, int len) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; int ret; /* TextNode *node = txtdoc->insert.curNode; TextNode *nextnode = NULL; if (!BE_LAST_NODE(node)) nextnode = TXTNODE_NEXT (node); */ if (!text || len <= 0) return -1; ret = textdoc_insert_text (txtdoc, text, len); teNodeChange (txtdoc, FALSE); teSetCaretPos (hWnd, ptedata); /* FIXME */ REFRESH_NODE(txtdoc->insert.curNode); /* if (set_line_size (hWnd, ptedata, node) < 0) REFRESH_NODE(node); if (nextnode) set_line_size (hWnd, ptedata, TXTNODE_PREV(nextnode)); else set_line_size (hWnd, ptedata, LASTNODE(txtdoc)); */ NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CHANGE); ptedata->cont_dirty = TRUE; return ret; } #ifdef _MGHAVE_CLIPBOARD /* * textedit_insert_cbtext : inserts clipboard texts into the current insertion point */ static int textedit_insert_cbtext (HWND hWnd, PTEDATA ptedata) { int inserting; unsigned char *txtBuffer; inserting = GetClipBoardDataLen (CBNAME_TEXT); txtBuffer = (unsigned char *)FixStrAlloc (inserting); GetClipBoardData (CBNAME_TEXT, txtBuffer, inserting); textedit_insert_text (hWnd, (const char*)txtBuffer, inserting); FreeFixStr((char*)txtBuffer); return inserting; } #endif /* * initialize text edit object structure */ static int teInitData (HWND hWnd, PTEDATA ptedata) { int pageval; TextDoc *txtdoc = &ptedata->txtdoc; ptedata->hardLimit = -1; ptedata->curlen = 0; /* init scrollview object */ scrollview_init (hWnd, &ptedata->svdata); /* change default move_content function */ ptedata->svdata.scrdata.move_content = textedit_set_svlist; ptedata->nLineHeight = DEF_LINE_HEIGHT; ptedata->nLineAboveH = DEF_LINE_ABOVE_H; ptedata->nLineBaseH = DEF_LINE_BASE_H; #ifdef _TITLE_SUPPORT if (GetWindowStyle(hWnd) & ES_TITLE) { ptedata->title = FixStrAlloc (DEF_TITLE_LEN+1); ptedata->title[0] = '\0'; ptedata->titleIndent = 0; } else { ptedata->title = NULL; ptedata->titleIndent = 0; } #endif scrolled_set_scrollval (ptescr, 0, ptedata->nLineHeight); pageval = (ptescr->visibleHeight / ptedata->nLineHeight ) * ptedata->nLineHeight; scrolled_set_scrollpageval (ptescr, 0, pageval); ptedata->lnChar = 0; ptedata->caretShape = ED_CARETSHAPE_LINE; teResetData (ptedata); scrollview_set_itemops (&ptedata->svdata, (SVITEMOPS*)&textedit_iops); ptedata->drawSelected = NULL; ptedata->getCaretWidth = NULL; /* init text document object */ INIT_LIST_HEAD(&txtdoc->queue); memset (&txtdoc->insert, 0, sizeof(txtdoc->insert)); #ifdef _SELECT_SUPPORT txtdoc->selection.curNode = NULL; #endif textdoc_reset (hWnd, txtdoc); /* FIXME, where to put? */ SetWindowAdditionalData2 (hWnd, (DWORD) ptedata); scrollview_freeze (hWnd, &ptedata->svdata, TRUE); textdoc_settext (txtdoc, GetWindowCaption(hWnd)); scrollview_freeze (hWnd, &ptedata->svdata, FALSE); /*Init temp StrBuffer*/ testr_alloc (&(ptedata->tebuff), 128, 256); return 0; } #ifdef _UNDO_SUPPORT /* static void teUndoBackup (TextDoc *txtdoc) { PTEDATA ptedata = (PTEDATA)GetWindowAdditionalData2 ((HWND)txtdoc->fn_data); TextNode *curnode = txtdoc->insert.curNode; if ( (ptedata->act_count %= ACTION_COUNT) == 0 ) { BACKUP_DATA(); ptedata->bkIns = txtdoc->insert; ptedata->bkSel = txtdoc->selection; testr_free (ptedata->bkBuff); free (ptedata->bkBuff); ptedata->bkBuff = testr_dup (&curnode->content); printf ("back up : %s\n", ptedata->bkBuff->string); ptedata->act_count = 0; } ptedata->act_count ++; } */ static int te_init_undo (HWND hWnd, PTEDATA ptedata) { ptedata->undo_depth = DEF_UNDO_DEPTH; ptedata->bkData = calloc (1, sizeof(BKDATA)*ptedata->undo_depth); if (!ptedata->bkData) return -1; ptedata->undo_level = -1; ptedata->act_count = 0; return 0; } #endif static int textedit_init (HWND hWnd, PTEDATA ptedata) { HDC hdc ; if (!ptedata) return -1; teInitData (hWnd, ptedata); if (BE_WRAP(hWnd)) ShowScrollBar (hWnd, SB_HORZ, FALSE); // create caret with the max font width, so that the // caret bitmap memery is enought for caret shape changed. hdc = GETDC (hWnd); CreateCaret (hWnd, NULL, GetMaxFontWidth(hdc), ptedata->nLineHeight - ptedata->nLineAboveH- ptedata->nLineBaseH); RELEASEDC (hWnd, hdc); /* SetFocus (hWnd); */ mySetCaretPos (hWnd, ptedata->des_caret_x, 0 + ptedata->nLineAboveH); #ifdef _UNDO_SUPPORT if (te_init_undo (hWnd, ptedata) < 0) return -1; #endif return 0; } /* * destroy a textedit */ static int textedit_destroy (HWND hWnd, PTEDATA ptedata) { #ifdef _TITLE_SUPPORT if (GetWindowStyle(hWnd) & ES_TITLE) { FreeFixStr (ptedata->title); } #endif DestroyCaret (hWnd); scrollview_destroy (&ptedata->svdata); textdoc_free (&ptedata->txtdoc); testr_free (&(ptedata->tebuff)); ptedata->hardLimit = -1; ptedata->curlen = 0; return 0; } /* --------------------------------------------------------------------------------- */ #define RETURN_DIRECT return DefaultControlProc(hWnd, message, wParam, lParam) static void init_bufdc (HWND hWnd, PTEDATA ptedata) { RECT rc; HDC hdc; GetClientRect(hWnd, &rc); if (ptedata->buf_dc != 0) { int w = GetGDCapability(ptedata->buf_dc, GDCAP_MAXX) + 1; int h = GetGDCapability(ptedata->buf_dc, GDCAP_MAXY) + 1; if (w == RECTW(rc) && h == RECTH(rc)) { return; } DeleteCompatibleDC(ptedata->buf_dc); } hdc = GetClientDC(hWnd); ptedata->buf_dc = CreateCompatibleDCEx(hdc, RECTW(rc), RECTH(rc)); if (ptedata->buf_dc == HDC_INVALID) { fprintf(stderr, "create textedit buf_dc error!\n"); } DefaultControlProc (hWnd, MSG_ERASEBKGND, (WPARAM)ptedata->buf_dc, 0); ReleaseDC(hdc); } static LRESULT TextEditCtrlProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PTEDATA ptedata = NULL; if (message != MSG_CREATE) ptedata = (PTEDATA) GetWindowAdditionalData2 (hWnd); switch (message) { case MSG_CREATE: { if (!(ptedata = (PTEDATA) calloc (1, sizeof (TEDATA)))) return -1; textedit_init (hWnd, ptedata); ptedata->caretShape = 1; RETURN_DIRECT; } case MSG_DESTROY: { textedit_destroy (hWnd, ptedata); free (ptedata); DeleteCompatibleDC(ptedata->buf_dc); RETURN_DIRECT; } case MSG_SETCURSOR: if (GetWindowStyle(hWnd) & WS_DISABLED) { SetCursor (GetSystemCursor (IDC_ARROW)); return 0; } break; case MSG_SETFOCUS: ptedata->flags |= TEST_FOCUSED; ptedata->cont_dirty = FALSE; mySetCaretPos (hWnd, -1, -1); ptedata->ex_flags |= TEST_EX_SETFOCUS; ptedata->ex_flags |= ~TEST_EX_KILLFOCUS; NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_SETFOCUS); InvalidateRect (hWnd, NULL, TRUE); break; case MSG_KILLFOCUS: { DWORD dwStyle = GetWindowStyle (hWnd); ptedata->flags &= ~TEST_FOCUSED; HideCaret (hWnd); if (ptedata->cont_dirty == TRUE) { NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CONTCHANGED); ptedata->cont_dirty = FALSE; } ptedata->ex_flags &= ~TEST_EX_SETFOCUS; ptedata->ex_flags |= TEST_EX_KILLFOCUS; NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_KILLFOCUS); if (!(dwStyle & ES_NOHIDESEL)) { te_unselect_all (hWnd, ptedata, FALSE); } InvalidateRect (hWnd, NULL, TRUE); break; } case MSG_DOESNEEDIME: if (GetWindowStyle(hWnd) & ES_READONLY) return IME_WINDOW_TYPE_READONLY; else return IME_WINDOW_TYPE_EDITABLE; case MSG_GETDLGCODE: return DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS | DLGC_WANTENTER; case MSG_LBUTTONDBLCLK: NotifyParent (hWnd, GetDlgCtrlID (hWnd), EN_DBLCLK); break; #ifdef TEXTEDIT_BUFDC case MSG_ERASEBKGND: SelectClipRect(ptedata->buf_dc, NULL); DefaultControlProc (hWnd, MSG_ERASEBKGND, (WPARAM)ptedata->buf_dc, lParam); return 0; case MSG_CHANGESIZE: DefaultControlProc (hWnd, MSG_CHANGESIZE, wParam, lParam); init_bufdc (hWnd, ptedata); return 0; #endif case MSG_PAINT: { HDC hdc = BeginPaint (hWnd); RECT rcDraw, rcVis; scrolled_get_content_rect (ptescr, &rcDraw); scrolled_visible_to_window (ptescr, &rcDraw.left, &rcDraw.top); scrolled_visible_to_window (ptescr, &rcDraw.right, &rcDraw.bottom); #ifndef TEXTEDIT_BUFDC scrolled_get_visible_rect (ptescr, &rcVis); ClipRectIntersect (hdc, &rcVis); tePaint (hWnd, hdc, &rcDraw); scrollview_draw (hWnd, hdc, &ptedata->svdata); #else scrolled_get_visible_rect (ptescr, &rcVis); SelectClipRect(ptedata->buf_dc, NULL); ClipRectIntersect (ptedata->buf_dc, &rcVis); tePaint (hWnd, ptedata->buf_dc, &rcDraw); scrollview_draw (hWnd, ptedata->buf_dc, &ptedata->svdata); BitBlt(ptedata->buf_dc, 0, 0, 0, 0, hdc, 0, 0, 0); #endif if ((ptedata->ex_flags & TEST_EX_SETFOCUS)){ ptedata->ex_flags &= ~TEST_EX_SETFOCUS; } else if (ptedata->ex_flags & TEST_EX_KILLFOCUS) { ptedata->ex_flags &= ~TEST_EX_KILLFOCUS; } EndPaint (hWnd, hdc); return 0; } case MSG_KEYDOWN: { #ifdef _TEXTEDIT_DEBUG if (wParam == SCANCODE_F1) { /* print_selected (&ptedata->txtdoc); */ printf ("current node item y = %d\n", ptedata->curItemY); printf ("current total item h = %d\n", ptedata->svdata.svlist.nTotalItemH); } #endif switch (wParam) { case SCANCODE_REMOVE: teOnChar (hWnd, ptedata, 127); return 0; case SCANCODE_CURSORBLOCKLEFT: case SCANCODE_CURSORBLOCKRIGHT: case SCANCODE_HOME: case SCANCODE_END: te_cursor_shift (hWnd, ptedata, wParam, lParam & KS_SHIFT); return 0; case SCANCODE_CURSORBLOCKUP: te_cursor_updown (hWnd, ptedata, -1, lParam & KS_SHIFT); return 0; case SCANCODE_CURSORBLOCKDOWN: te_cursor_updown (hWnd, ptedata, 1, lParam & KS_SHIFT); return 0; #if defined(_MGHAVE_CLIPBOARD) #if defined(_SELECT_SUPPORT) case SCANCODE_C: if (lParam & KS_CTRL) { textdoc_copy_to_cb (ptedata, NULL, 0, TRUE); } return 0; #endif case SCANCODE_V: if (lParam & KS_CTRL) { return textedit_insert_cbtext (hWnd, ptedata); } return 0; #endif #ifdef _SELECT_SUPPORT case SCANCODE_A: if (lParam & KS_CTRL) { te_select_all (hWnd, ptedata); } return 0; #ifdef _MGHAVE_CLIPBOARD case SCANCODE_X: if (lParam & KS_CTRL && ptedata->txtdoc.selection.curNode) { textdoc_copy_to_cb (ptedata, NULL, 0, TRUE); teOnChar (hWnd, ptedata, 127); } return 0; #endif #endif #ifdef _UNDO_SUPPORT /* case SCANCODE_Z: if (lParam & KS_CTRL) teUndo (hWnd, ptedata); return 0; case SCANCODE_R: if (lParam & KS_CTRL) teRedo (hWnd, ptedata); return 0; */ #endif } /* end switch keydown */ break; } case MSG_UTF8CHAR: _ERR_PRINTF ("CONTROL>EDIT: MSG_UTF8CHAR is not implemented.\n"); break; case MSG_CHAR: if (wParam == 127) /* BS */ wParam = '\b'; teOnChar (hWnd, ptedata, wParam); return 0; case MSG_LBUTTONUP: #ifdef _SELECT_SUPPORT if (GetCapture() == hWnd) ReleaseCapture(); if (ptedata->flags & TEST_SELECT) ptedata->flags &= ~TEST_SELECT; if (ptedata->flags & TEST_MOVE) ptedata->flags &= ~TEST_MOVE; #endif NotifyParent (hWnd, GetDlgCtrlID(hWnd), EN_CLICKED); break; case MSG_LBUTTONDOWN: { int mouseX = LOSWORD (lParam); int mouseY = HISWORD (lParam); HSVITEM hsvi = 0; int nItem, item_y = 0; POINT pt; #ifdef _SELECT_SUPPORT if (!GetCapture()) { SetCapture(hWnd); ptedata->flags |= TEST_SELECT; } #endif scrolled_window_to_visible (ptescr, &mouseX, &mouseY); scrolled_visible_to_content (ptescr, &mouseX, &mouseY); pt.x = mouseX; pt.y = mouseY; nItem = scrollview_is_in_item (&ptedata->svdata, mouseY, &hsvi, &item_y); if (nItem >= 0) { scrolled_make_pos_visible (hWnd, ptescr, pt.x, pt.y); pt.y -= item_y; } TE_OnMouseDown (hWnd, hsvi, &pt, item_y, wParam & KS_SHIFT); return 0; } #ifdef _SELECT_SUPPORT case MSG_MOUSEMOVE: { int mouseX = LOSWORD (lParam); int mouseY = HISWORD (lParam); POINT pt; RECT rcVis; if (GetCapture() != hWnd || !(ptedata->flags & TEST_SELECT)) break; ScreenToClient (hWnd, &mouseX, &mouseY); scrolled_get_visible_rect (ptescr, &rcVis); if (!PtInRect (&rcVis, mouseX, mouseY)) { if ( !(ptedata->flags & TEST_MOVE) ) { ptedata->flags |= TEST_MOVE; SetAutoRepeatMessage (hWnd, MSG_MOUSEMOVE, 0, lParam); } } else { if ( ptedata->flags & TEST_MOVE ) { ptedata->flags &= ~TEST_MOVE; /* cancel repeat message */ SetAutoRepeatMessage (0, 0, 0, 0); } } scrolled_window_to_visible (ptescr, &mouseX, &mouseY); scrolled_visible_to_content (ptescr, &mouseX, &mouseY); pt.x = mouseX; pt.y = mouseY; te_cursor_move (hWnd, ptedata, &pt); break; } #endif case MSG_GETTEXTLENGTH: return textdoc_get_textlen(&ptedata->txtdoc); case MSG_GETTEXT: return textdoc_gettext (&ptedata->txtdoc, wParam, (unsigned char*)lParam); case MSG_SETTEXT: textedit_reset_content (hWnd, ptedata, (const char*)lParam, wParam); return 0; /* case MSG_FREEZECTRL: scrollview_freeze (hWnd, &ptedata->svdata, wParam); return 0; */ /* * FIXME,TODO */ case MSG_FONTCHANGED: { HDC hdc = GETDC (hWnd); TextDoc *txtdoc = &ptedata->txtdoc; TextNode *node = FIRSTNODE(txtdoc); DestroyCaret (hWnd); ptedata->nLineHeight = GetWindowFont(hWnd)->size + ptedata->nLineAboveH + ptedata->nLineBaseH; // create caret with the max font width, so that the // caret bitmap memery is enought for caret shape changed. CreateCaret (hWnd, NULL, GetMaxFontWidth(hdc), ptedata->nLineHeight - ptedata->nLineAboveH- ptedata->nLineBaseH); while (node) { txtdoc->change_cont (txtdoc, node); node = TXTNODE_NEXT(node); } teSetCaretPos (hWnd, ptedata); SelectFont(hdc, GetWindowFont(hWnd)); RELEASEDC (hWnd, hdc); if (ptedata->title) textedit_set_titletext (hWnd, ptedata, strlen(ptedata->title), ptedata->title); InvalidateRect (hWnd, NULL, TRUE); return 0; } case TEM_RESETCONTENT: textedit_reset_content (hWnd, ptedata, "", FALSE); return 0; #ifdef _TITLE_SUPPORT case EM_GETTITLETEXT: return textedit_get_titletext (hWnd, ptedata, wParam, (char *)lParam); case EM_SETTITLETEXT: return textedit_set_titletext (hWnd, ptedata, wParam, (const char *)lParam); #endif case EM_GETLINECOUNT: return scrollview_get_total_item_height(&ptedata->svdata) / ptedata->nLineHeight; case EM_GETLINEHEIGHT: return ptedata->nLineHeight; case EM_SETLINEHEIGHT: { int old_h = ptedata->nLineHeight, height = (int)wParam; int new_h; if (height <= 0) return -1; new_h = GetWindowFont(hWnd)->size + DEF_LINE_BASE_H + MIN_LINE_ABOVE_H; new_h = MAX (new_h, height); if (new_h == old_h) return -1; ptedata->nLineHeight = new_h; ptedata->nLineAboveH = new_h - GetWindowFont(hWnd)->size - ptedata->nLineBaseH; textedit_reset_content (hWnd, ptedata, "", FALSE); return old_h; } case EM_SETREADONLY: { if (wParam) IncludeWindowStyle (hWnd, ES_READONLY); else ExcludeWindowStyle (hWnd, ES_READONLY); return 0; } case EM_LIMITTEXT: { int newLimit = (int)wParam; if (newLimit >= 0) { if (ptedata->curlen > newLimit) { Ping (); return -1; } else { ptedata->hardLimit = newLimit; return 0; } } return -1; } case EM_GETLIMITTEXT: { return ptedata->hardLimit; } #ifdef _SELECT_SUPPORT #ifdef _MGHAVE_CLIPBOARD case EM_GETSEL: { char *buffer = (char *)lParam; int len = (int)wParam; if (!buffer && len != 0) return 0; return textdoc_copy_to_cb (ptedata, buffer, len, FALSE); } #endif case EM_SELECTALL: return te_select_all (hWnd, ptedata); #endif case EM_GETSELPOS: case EM_GETCARETPOS: { return textedit_get_pos_ex (hWnd, (int *)wParam, (int *)lParam, message==EM_GETSELPOS); } case EM_SETCARETPOS: case EM_SETSEL: { return textedit_set_pos_ex (hWnd, wParam, lParam, message == EM_SETSEL); } case EM_INSERTTEXT: { return textedit_insert_text (hWnd, (const char*)lParam, wParam); } #ifdef _MGHAVE_CLIPBOARD case EM_INSERTCBTEXT: { if (GetWindowStyle(hWnd) & ES_READONLY) return 0; return textedit_insert_cbtext (hWnd, ptedata); } #ifdef _SELECT_SUPPORT case EM_COPYTOCB: case EM_CUTTOCB: { int len; len = textdoc_copy_to_cb (ptedata, NULL, 0, TRUE); if (message == EM_CUTTOCB) teOnChar (hWnd, ptedata, 127); return len; } #endif #endif case EM_SETLFDISPCHAR: ptedata->lnChar = lParam; scrollview_refresh_content (&ptedata->svdata); return 0; /* must be setted before set text */ case EM_SETLINESEP: ptedata->txtdoc.lnsep = lParam; return 0; case EM_CHANGECARETSHAPE: { int old = ptedata->caretShape; ptedata->caretShape = wParam; textedit_refresh_caret (hWnd); return old; } case EM_SETDRAWSELECTFUNC: textedit_set_draw_selected(hWnd, (ED_DRAWSEL_FUNC)lParam); return 0; case EM_SETGETCARETWIDTHFUNC: textedit_set_caret_func (hWnd, (int(*)(HWND, int))lParam); return 0; case EM_REFRESHCARET: textedit_refresh_caret (hWnd); return 0; case EM_ENABLECARET: { if (wParam && (ptedata->flags & TEST_NOCARET)) { ptedata->flags &= ~TEST_NOCARET; if (lParam) teSetCaretPos (hWnd, ptedata); } else if (!wParam && !(ptedata->flags & TEST_NOCARET)) { ptedata->flags |= TEST_NOCARET; HideCaret (hWnd); } return 0; } case EM_GETNUMOFPARAGRAPHS: return textdoc_get_num_paragraphs (&ptedata->txtdoc); case EM_GETPARAGRAPHTEXT: { TEXTPOSINFO *info = (TEXTPOSINFO*)wParam; int start = info->start_pos; int len = info->copy_len; int index = info->paragraph_index; unsigned char* buff = (unsigned char*)(info->buff); return textdoc_get_paragraph_text (hWnd, &ptedata->txtdoc, index, start, len, buff); } case EM_GETPARAGRAPHLENGTH: return textdoc_get_paragraph_length (GetWindowFont(hWnd), &ptedata->txtdoc, wParam); case EM_UNDO: /* teUndo (hWnd, ptedata); */ return 0; case EM_REDO: /* teRedo (hWnd, ptedata); */ return 0; }/* end switch */ return ScrollViewCtrlProc (hWnd, message, wParam, lParam); } BOOL RegisterTextEditControl (void) { WNDCLASS WndClass; WndClass.spClassName = CTRL_TEXTEDIT; WndClass.dwStyle = WS_NONE; WndClass.dwExStyle = WS_EX_NONE; WndClass.hCursor = GetSystemCursor (IDC_IBEAM); #ifdef _MGSCHEMA_COMPOSITING WndClass.dwBkColor = GetWindowElementAttr (HWND_NULL, WE_BGC_WINDOW); #else WndClass.iBkColor = GetWindowElementPixelEx (HWND_NULL, HDC_SCREEN, WE_BGC_WINDOW); #endif WndClass.WinProc = TextEditCtrlProc; if (gui_AddNewControlClass (&WndClass) != ERR_OK) return FALSE; WndClass.spClassName = CTRL_MLEDIT; if (gui_AddNewControlClass (&WndClass) != ERR_OK) return FALSE; WndClass.spClassName = CTRL_MEDIT; return gui_AddNewControlClass (&WndClass) == ERR_OK; } #endif /* _MGCTRL_TEXTEDIT */