?? textedit.c
字號:
/*
** $Id: textedit.c,v 1.107 2004/10/18 00:49:28 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 "control.h"
#include "cliprect.h"
#include "internals.h"
#include "ctrlclass.h"
#ifdef _CTRL_TEXTEDIT
#include "ctrlmisc.h"
#include "scrolled.h"
#include "scrollview.h"
#include "text.h"
#include "textedit.h"
#ifdef _UNDO_SUPPORT
static void teUndoBackup (TextDoc *txtoc);
#endif
#undef _TEXTEDIT_DEBUG
//#define _TEXTEDIT_DEBUG
#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 = 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 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;
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 void
insert_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_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;
}
/* Gets 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;
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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -