mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-28 16:17:09 +08:00
3625 lines
102 KiB
C
3625 lines
102 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* 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
|
|
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
|
|
*/
|
|
/*
|
|
** 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#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 */
|
|
|