📄 textedit.c
字号:
/*** $Id: textedit.c,v 1.122 2005/02/05 03:54:33 snig Exp $**** textedit.c: text edit control**** Copyright (C) 2004 Feynman Software.** ** Current maintainer: Zhong Shuyi (zhongsy@minigui.org).**** Create date: 2004/03/01**** Note:** the textedit control is written from scratch** to replace the buggy medit control.**** The textedit control inherits scrollview.***//*** 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 2 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, write to the Free Software** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*//* * TODO * tab */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include "common.h"#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"#ifdef _CTRL_TEXTEDIT#include "ctrlmisc.h"#include "scrolled.h"#include "scrollview_impl.h"#include "text.h"#include "textedit_impl.h"#ifdef DT_WORDBREAK#undef DT_WORDBREAK#define DT_WORDBREAK 0#endif#ifdef _UNDO_SUPPORTstatic void teUndoBackup (TextDoc *txtoc);#endif#undef _TEXTEDIT_DEBUG//#define _TEXTEDIT_DEBUG#ifdef _TEXTEDIT_DEBUGstatic 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 = GetClientDC (hWnd); GetTabbedTextExtent(hdc, "\t", 1, &txtsize); ReleaseDC (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 : Sets 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 BOOLset_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; 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); return newnode;}/* * txtDelNode : deletes a text node */static void txtDelNode (TextDoc *txtdoc, TextNode *node){ /* 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;}/* * 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; if (!txtdoc || !content) return -1; /* free the old text content */ textdoc_free (txtdoc); ptmp = pLine = content; if (content) { 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); return 0;}static voidinsert_string (TextDoc *txtdoc, TextNode *curnode, int insert_pos, const char *newtext, int len){ StrBuffer *strbuff = &curnode->content; unsigned char *pLn, *pIns; 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); } else { pIns = strbuff->string + insert_pos; memmove (pIns + len, pIns, strbuff->txtlen+1 - insert_pos); pLn = testr_realloc (strbuff, strbuff->txtlen + len); } strbuff->txtlen += len;}#ifdef _SELECT_SUPPORTstatic 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;}/* Gets the start and end selection points in a text node */static voidget_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;}#endifstatic TextNode*insert_ln_sep (TextDoc *txtdoc, TextNode *curnode, int insert_pos, BOOL bChRn){ StrBuffer *strbuff = &curnode->content; TextNode *newnode; unsigned char *pIns; int len = bChRn ? 2 : 1; pIns = strbuff->string + insert_pos; newnode = txtAddNode ( txtdoc, 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(pIns, CH_RN, len); else *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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -