From d488f3f1de7774f892f0195a2584c4e3e9d581a1 Mon Sep 17 00:00:00 2001 From: Krzysztof Foltman Date: Sat, 5 Mar 2005 11:19:14 +0000 Subject: [PATCH] Initial implementation of riched20. --- configure | 3 +- configure.ac | 1 + dlls/Makefile.in | 13 + dlls/riched20/.cvsignore | 3 + dlls/riched20/Makefile.in | 25 + dlls/riched20/caret.c | 959 ++++++++++++++++++++++++++++++++++ dlls/riched20/context.c | 37 ++ dlls/riched20/editor.c | 998 ++++++++++++++++++++++++++++++++++++ dlls/riched20/editor.h | 209 ++++++++ dlls/riched20/editstr.h | 245 +++++++++ dlls/riched20/list.c | 191 +++++++ dlls/riched20/paint.c | 359 +++++++++++++ dlls/riched20/para.c | 358 +++++++++++++ dlls/riched20/riched20.spec | 9 + dlls/riched20/richole.c | 36 ++ dlls/riched20/row.c | 84 +++ dlls/riched20/run.c | 672 ++++++++++++++++++++++++ dlls/riched20/string.c | 318 ++++++++++++ dlls/riched20/style.c | 397 ++++++++++++++ dlls/riched20/undo.c | 261 ++++++++++ dlls/riched20/wrap.c | 415 +++++++++++++++ 21 files changed, 5592 insertions(+), 1 deletion(-) create mode 100644 dlls/riched20/.cvsignore create mode 100644 dlls/riched20/Makefile.in create mode 100644 dlls/riched20/caret.c create mode 100644 dlls/riched20/context.c create mode 100644 dlls/riched20/editor.c create mode 100644 dlls/riched20/editor.h create mode 100644 dlls/riched20/editstr.h create mode 100644 dlls/riched20/list.c create mode 100644 dlls/riched20/paint.c create mode 100644 dlls/riched20/para.c create mode 100644 dlls/riched20/riched20.spec create mode 100644 dlls/riched20/richole.c create mode 100644 dlls/riched20/row.c create mode 100644 dlls/riched20/run.c create mode 100644 dlls/riched20/string.c create mode 100644 dlls/riched20/style.c create mode 100644 dlls/riched20/undo.c create mode 100644 dlls/riched20/wrap.c diff --git a/configure b/configure index a8ff5472479..66a7c850644 100755 --- a/configure +++ b/configure @@ -19988,7 +19988,7 @@ MAKE_LIB_RULES=libs/Makelib.rules MAKE_PROG_RULES=programs/Makeprog.rules - ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/advpack/Makefile dlls/advpack/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/cryptdll/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/itss/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/lzexpand/tests/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msacm/tests/Makefile dlls/mscms/Makefile dlls/mscms/tests/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msi/tests/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/rsaenh/Makefile dlls/rsaenh/tests/Makefile dlls/secur32/Makefile dlls/sensapi/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/stdole32.tlb/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/wtsapi32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile" + ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/advpack/Makefile dlls/advpack/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/cryptdll/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/itss/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/lzexpand/tests/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msacm/tests/Makefile dlls/mscms/Makefile dlls/mscms/tests/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msi/tests/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/riched20/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/rsaenh/Makefile dlls/rsaenh/tests/Makefile dlls/secur32/Makefile dlls/sensapi/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/stdole32.tlb/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/wtsapi32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile" cat >confcache <<\_ACEOF @@ -20650,6 +20650,7 @@ do "dlls/quartz/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/quartz/Makefile" ;; "dlls/quartz/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/quartz/tests/Makefile" ;; "dlls/rasapi32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/rasapi32/Makefile" ;; + "dlls/riched20/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/riched20/Makefile" ;; "dlls/richedit/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/richedit/Makefile" ;; "dlls/rpcrt4/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/rpcrt4/Makefile" ;; "dlls/rpcrt4/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/rpcrt4/tests/Makefile" ;; diff --git a/configure.ac b/configure.ac index b06b65a5dcc..600393cfb64 100644 --- a/configure.ac +++ b/configure.ac @@ -1634,6 +1634,7 @@ dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile +dlls/riched20/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile diff --git a/dlls/Makefile.in b/dlls/Makefile.in index b7a5503f5dd..812889126f1 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -108,6 +108,7 @@ BASEDIRS = \ qcap \ quartz \ rasapi32 \ + riched20 \ richedit \ rpcrt4 \ rsabase \ @@ -351,6 +352,7 @@ SYMLINKS_SO = \ qcap.dll.so \ quartz.dll.so \ rasapi32.dll.so \ + riched20.dll.so \ riched32.dll.so \ rpcrt4.dll.so \ rsabase.dll.so \ @@ -781,6 +783,9 @@ rasapi32.dll.so: rasapi32/rasapi32.dll.so rasapi16.dll.so : rasapi32.dll.so $(RM) $@ && $(LN_S) rasapi32.dll.so $@ +riched20.dll.so: riched20/riched20.dll.so + $(RM) $@ && $(LN_S) riched20/riched20.dll.so $@ + riched32.dll.so: richedit/riched32.dll.so $(RM) $@ && $(LN_S) richedit/riched32.dll.so $@ @@ -1092,6 +1097,7 @@ IMPORT_LIBS = \ libqcap.$(IMPLIBEXT) \ libquartz.$(IMPLIBEXT) \ librasapi32.$(IMPLIBEXT) \ + libriched20.$(IMPLIBEXT) \ libriched32.$(IMPLIBEXT) \ librpcrt4.$(IMPLIBEXT) \ librsabase.$(IMPLIBEXT) \ @@ -1590,6 +1596,11 @@ librasapi32.def: rasapi32/rasapi32.spec.def librasapi32.a: rasapi32/rasapi32.spec.def $(DLLTOOL) -k -l $@ -d rasapi32/rasapi32.spec.def +libriched20.def: riched20/riched20.spec.def + $(RM) $@ && $(LN_S) riched20/riched20.spec.def $@ +libriched20.a: riched20/riched20.spec.def + $(DLLTOOL) -k -l $@ -d riched20/riched20.spec.def + libriched32.def: richedit/riched32.spec.def $(RM) $@ && $(LN_S) richedit/riched32.spec.def $@ libriched32.a: richedit/riched32.spec.def @@ -1880,6 +1891,7 @@ psapi/psapi.spec.def: $(WINEBUILD) qcap/qcap.spec.def: $(WINEBUILD) quartz/quartz.spec.def: $(WINEBUILD) rasapi32/rasapi32.spec.def: $(WINEBUILD) +riched20/riched20.spec.def: $(WINEBUILD) richedit/riched32.spec.def: $(WINEBUILD) rpcrt4/rpcrt4.spec.def: $(WINEBUILD) rsabase/rsabase.spec.def: $(WINEBUILD) @@ -2032,6 +2044,7 @@ psapi/psapi.dll.so: psapi qcap/qcap.dll.so: qcap quartz/quartz.dll.so: quartz rasapi32/rasapi32.dll.so: rasapi32 +riched20/riched20.dll.so: riched20 richedit/riched32.dll.so: richedit rpcrt4/rpcrt4.dll.so: rpcrt4 rsabase/rsabase.dll.so: rsabase diff --git a/dlls/riched20/.cvsignore b/dlls/riched20/.cvsignore new file mode 100644 index 00000000000..0ab6d3fd3b9 --- /dev/null +++ b/dlls/riched20/.cvsignore @@ -0,0 +1,3 @@ +Makefile +riched20.dll.dbg.c +riched20.spec.def diff --git a/dlls/riched20/Makefile.in b/dlls/riched20/Makefile.in new file mode 100644 index 00000000000..124e5f7c350 --- /dev/null +++ b/dlls/riched20/Makefile.in @@ -0,0 +1,25 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = riched20.dll +IMPORTS = user32 gdi32 kernel32 + +C_SRCS = \ + caret.c \ + context.c \ + editor.c \ + list.c \ + paint.c \ + para.c \ + richole.c \ + row.c \ + run.c \ + string.c \ + style.c \ + undo.c \ + wrap.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/dlls/riched20/caret.c b/dlls/riched20/caret.c new file mode 100644 index 00000000000..89b447a5f32 --- /dev/null +++ b/dlls/riched20/caret.c @@ -0,0 +1,959 @@ +/* + * RichEdit - Caret and selection functions. + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +void ME_GetSelection(ME_TextEditor *editor, int *from, int *to) +{ + *from = ME_GetCursorOfs(editor, 0); + *to = ME_GetCursorOfs(editor, 1); + + if (*from > *to) + { + int tmp = *from; + *from = *to; + *to = tmp; + } +} + +int ME_GetTextLength(ME_TextEditor *editor) +{ + return ME_CharOfsFromRunOfs(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun), 0); +} + +void ME_SetSelection(ME_TextEditor *editor, int from, int to) +{ + if (from == 0 && to == -1) + { + editor->pCursors[1].pRun = ME_FindItemFwd(editor->pBuffer->pFirst, diRun); + editor->pCursors[1].nOffset = 0; + editor->pCursors[0].pRun = ME_FindItemBack(editor->pBuffer->pLast, diRun); + editor->pCursors[0].nOffset = 0; + ME_Repaint(editor); + ME_ClearTempStyle(editor); + return; + } + if (from == -1) + { + editor->pCursors[1] = editor->pCursors[0]; + ME_Repaint(editor); + ME_ClearTempStyle(editor); + return; + } + if (from>to) + { + int tmp = from; + from = to; + to = tmp; + } + ME_RunOfsFromCharOfs(editor, from, &editor->pCursors[1].pRun, &editor->pCursors[1].nOffset); + ME_RunOfsFromCharOfs(editor, to, &editor->pCursors[0].pRun, &editor->pCursors[0].nOffset); +} + +void ME_MoveCaret(ME_TextEditor *editor) +{ + HDC hDC = GetDC(editor->hWnd); + ME_Context c; + + ME_Cursor *pCursor = &editor->pCursors[0]; + ME_DisplayItem *pCursorRun = pCursor->pRun; + ME_DisplayItem *pSizeRun = pCursor->pRun; + + ME_InitContext(&c, editor, hDC); + assert(!pCursor->nOffset || !editor->bCaretAtEnd); + + if (pCursorRun->type == diRun) { + ME_DisplayItem *row = ME_FindItemBack(pCursorRun, diStartRowOrParagraph); + if (row) { + ME_DisplayItem *run = pCursorRun; + ME_DisplayItem *para; + SIZE sz = {0, 0}; + if (!pCursor->nOffset && !editor->bCaretAtEnd) + { + ME_DisplayItem *prev = ME_FindItemBack(pCursorRun, diRunOrStartRow); + if (prev->type == diRun) + pSizeRun = prev; + } + assert(row->type == diStartRow); /* paragraph -> run without start row ?*/ + para = ME_FindItemBack(row, diParagraph); + if (editor->bCaretAtEnd && !pCursor->nOffset && + run == ME_FindItemFwd(row, diRun)) + { + ME_DisplayItem *tmp = ME_FindItemBack(row, diRunOrParagraph); + if (tmp->type == diRun) + { + row = ME_FindItemBack(tmp, diStartRow); + pSizeRun = run = tmp; + sz = ME_GetRunSize(&c, &run->member.run, ME_StrLen(run->member.run.strText)); + } + } + if (pCursor->nOffset && !(run->member.run.nFlags & MERF_SKIPPED)) { + sz = ME_GetRunSize(&c, &run->member.run, pCursor->nOffset); + } + CreateCaret(editor->hWnd, NULL, 0, pSizeRun->member.run.nAscent+pSizeRun->member.run.nDescent); + SetCaretPos(run->member.run.pt.x+sz.cx, + para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-GetScrollPos(editor->hWnd, SB_VERT)); + } else { + assert(0 == "Wrapped paragraph run without a row?"); + CreateCaret(editor->hWnd, NULL, 0, 10); + SetCaretPos(0,0); + } + } + else { + assert(0 == "Cursor not on a run"); + CreateCaret(editor->hWnd, NULL, 0, 10); /* FIXME use global font */ + SetCaretPos(0,0); + } + ME_DestroyContext(&c); + ReleaseDC(editor->hWnd, hDC); +} + +void ME_ShowCaret(ME_TextEditor *ed) +{ + ME_MoveCaret(ed); + ShowCaret(ed->hWnd); +} + +void ME_HideCaret(ME_TextEditor *ed) +{ + HideCaret(ed->hWnd); + DestroyCaret(); +} + +void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, + int nChars) +{ + ME_Cursor c; + int shift = 0; + + while(nChars > 0) + { + ME_Run *run; + ME_CursorFromCharOfs(editor, nOfs, &c); + run = &c.pRun->member.run; + if (run->nFlags & MERF_ENDPARA) { + if (!ME_FindItemFwd(c.pRun, diParagraph)) + { + return; + } + ME_JoinParagraphs(editor, ME_GetParagraph(c.pRun)); + /* ME_SkipAndPropagateCharOffset(p->pRun, shift); */ + ME_CheckCharOffsets(editor); + nChars--; + continue; + } + else + { + ME_Cursor cursor; + int nIntendedChars = nChars; + int nCharsToDelete = nChars; + int i; + int loc = c.nOffset; + + ME_FindItemBack(c.pRun, diParagraph)->member.para.nFlags &= ~MEPF_WRAPPED; + + cursor = c; + ME_StrRelPos(run->strText, loc, &nChars); + /* nChars is the number of characters that should be deleted from the + FOLLOWING runs (these AFTER cursor.pRun) + nCharsToDelete is a number of chars to delete from THIS run */ + nCharsToDelete -= nChars; + shift -= nCharsToDelete; + TRACE("Deleting %d (intended %d-remaning %d) chars at %d in '%s' (%d)\n", + nCharsToDelete, nIntendedChars, nChars, c.nOffset, + debugstr_w(run->strText->szData), run->strText->nLen); + + if (!c.nOffset && ME_StrVLen(run->strText) == nCharsToDelete) + { + /* undo = reinsert whole run */ + /* nOfs is a character offset (from the start of the document + to the current (deleted) run */ + ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); + if (pUndo) + pUndo->di.member.run.nCharOfs = nOfs; + } + else + { + /* undo = reinsert partial run */ + ME_UndoItem *pUndo = ME_AddUndoItem(editor, diUndoInsertRun, c.pRun); + if (pUndo) { + ME_DestroyString(pUndo->di.member.run.strText); + pUndo->di.member.run.nCharOfs = nOfs; + pUndo->di.member.run.strText = ME_MakeStringN(run->strText->szData+c.nOffset, nCharsToDelete); + } + } + TRACE("Post deletion string: %s (%d)\n", debugstr_w(run->strText->szData), run->strText->nLen); + TRACE("Shift value: %d\n", shift); + ME_StrDeleteV(run->strText, c.nOffset, nCharsToDelete); + + /* update cursors (including c) */ + for (i=-1; inCursors; i++) { + ME_Cursor *pThisCur = editor->pCursors + i; + if (i == -1) pThisCur = &c; + if (pThisCur->pRun == cursor.pRun) { + if (pThisCur->nOffset > cursor.nOffset) { + if (pThisCur->nOffset-cursor.nOffset < nCharsToDelete) + pThisCur->nOffset = cursor.nOffset; + else + pThisCur->nOffset -= nCharsToDelete; + assert(pThisCur->nOffset >= 0); + assert(pThisCur->nOffset <= ME_StrVLen(run->strText)); + } + if (pThisCur->nOffset == ME_StrVLen(run->strText)) + { + pThisCur->pRun = ME_FindItemFwd(pThisCur->pRun, diRunOrParagraphOrEnd); + assert(pThisCur->pRun->type == diRun); + pThisCur->nOffset = 0; + } + } + } + + /* c = updated data now */ + + if (c.pRun == cursor.pRun) + ME_SkipAndPropagateCharOffset(c.pRun, shift); + else + ME_PropagateCharOffset(c.pRun, shift); + + if (!ME_StrVLen(cursor.pRun->member.run.strText)) + { + TRACE("Removing useless run\n"); + ME_Remove(cursor.pRun); + ME_DestroyDisplayItem(cursor.pRun); + } + + shift = 0; + /* + ME_CheckCharOffsets(editor); + */ + continue; + } + } +} + +void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, + int nChars) +{ + assert(nCursor>=0 && nCursornCursors); + ME_InternalDeleteText(editor, ME_GetCursorOfs(editor, nCursor), nChars); +} + +static WCHAR wszSpace[] = {' ', 0}; + +/* FIXME this is temporary, just to have something to test how bad graphics handler is */ +void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor) +{ + ME_Cursor *pCursor = &editor->pCursors[nCursor]; + ME_DisplayItem *pItem = NULL; + ME_DisplayItem *pNewRun = NULL; + ME_Style *pStyle = ME_GetInsertStyle(editor, nCursor); + ME_UndoItem *pUndo; + + /* FIXME no no no */ + if (ME_IsSelection(editor)) + ME_DeleteSelection(editor); + + pUndo = ME_AddUndoItem(editor, diUndoDeleteRun, NULL); + if (pUndo) { + pUndo->nStart = pCursor->nOffset + pCursor->pRun->member.run.nCharOfs + ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs; + pUndo->nLen = 1; + } + if (pCursor->nOffset) + { + ME_SplitRunSimple(editor, pCursor->pRun, pCursor->nOffset); + } + pItem = pCursor->pRun; + pNewRun = ME_MakeRun(pStyle, ME_MakeStringN(wszSpace, 1), MERF_GRAPHICS); + pNewRun->member.run.nCharOfs = pCursor->pRun->member.run.nCharOfs; + ME_InsertBefore(pCursor->pRun, pNewRun); + ME_PropagateCharOffset(pItem, 1); + ME_CheckCharOffsets(editor); + ME_SendSelChange(editor); +} + +void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, + const WCHAR *str, int len, ME_Style *style) +{ + const WCHAR *pos; + ME_Cursor *p = NULL; + + assert(style); + editor->bCaretAtEnd = FALSE; + + /* + if (!style) + style = ME_GetInsertStyle(editor, nCursor); + else + ME_AddRefStyle(style); + */ + ME_AddRefStyle(style); + + /* FIXME really HERE ? */ + if (ME_IsSelection(editor)) + ME_DeleteSelection(editor); + + assert(nCursor>=0 && nCursornCursors); + if (len == -1) + len = lstrlenW(str); + pos = str; + /* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */ + while(pos-str < len && *pos != '\r' && *pos != '\n') + pos++; + /* handle EOLs */ + if (pos-str < len) { + ME_DisplayItem *tp, *end_run; + ME_Paragraph *para; + ME_Style *tmp_style; + if (pos!=str) + ME_InsertTextFromCursor(editor, nCursor, str, pos-str, style); + p = &editor->pCursors[nCursor]; + tp = ME_FindItemBack(p->pRun, diParagraph); + para = &tp->member.para; + assert(tp); + if (p->nOffset) { + ME_SplitRunSimple(editor, p->pRun, p->nOffset); + p = &editor->pCursors[nCursor]; + } + tmp_style = ME_GetInsertStyle(editor, nCursor); + /* ME_SplitParagraph increases style refcount */ + tp = ME_SplitParagraph(editor, p->pRun, p->pRun->member.run.style); + p->pRun = ME_FindItemFwd(tp, diRun); + end_run = ME_FindItemBack(tp, diRun); + ME_ReleaseStyle(end_run->member.run.style); + end_run->member.run.style = tmp_style; + p->nOffset = 0; + if(pos-str < len && *pos =='\r') + pos++; + if(pos-str < len && *pos =='\n') + pos++; + if(pos-str < len) { + ME_InsertTextFromCursor(editor, nCursor, pos, len-(pos-str), style); + } + ME_ReleaseStyle(style); + return; + } + p = &editor->pCursors[nCursor]; + if (style) { + ME_DisplayItem *pNewRun = NULL; + + assert(p->pRun->type == diRun); + pNewRun = ME_MakeRun(style, ME_MakeStringN(str, len), 0); /* addrefs style */ + ME_InsertRun(editor, ME_CharOfsFromRunOfs(editor, p->pRun, p->nOffset), pNewRun); + ME_DestroyDisplayItem(pNewRun); + ME_ReleaseStyle(style); + return; + } else { + assert(0); + } +} + +BOOL ME_ArrowLeft(ME_TextEditor *editor, ME_Cursor *p) +{ + if (p->nOffset) { + p->nOffset = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, -1); + return TRUE; + } + else + { + ME_DisplayItem *pRun = ME_FindItemBack(p->pRun, diRunOrParagraph); + assert(pRun); + if (pRun->type == diRun) { + p->pRun = pRun; + assert(p->pRun->type == diRun); + p->nOffset = pRun->member.run.strText->nLen; + if (p->nOffset) { + p->nOffset = ME_StrRelPos2(pRun->member.run.strText, p->nOffset, -1); + return TRUE; + } + else + assert(0); + } + if (pRun->type == diParagraph) + { + if (pRun->member.para.prev_para->type == diTextStart) + return FALSE; + assert(pRun->member.para.prev_para->type == diParagraph); + pRun = ME_FindItemBack(pRun, diRunOrParagraph); + /* every paragraph ought to have at least one run */ + assert(pRun && pRun->type == diRun); + assert(pRun->member.run.nFlags & MERF_ENDPARA); + p->pRun = pRun; + p->nOffset = 0; + return TRUE; + } + assert(0); + } +} + +BOOL ME_ArrowRight(ME_TextEditor *editor, ME_Cursor *p) +{ + int new_ofs = ME_StrRelPos2(p->pRun->member.run.strText, p->nOffset, 1); + if (new_ofspRun->member.run.strText->nLen) { + p->nOffset = new_ofs; + } + else + { + ME_DisplayItem *pRun = ME_FindItemFwd(p->pRun, diRun); + if (pRun) { + p->pRun = pRun; + assert(p->pRun->type == diRun); + p->nOffset = 0; + } + } + return TRUE; +} + +int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor) +{ + ME_Cursor *pCursor = &editor->pCursors[nCursor]; + + return ME_GetParagraph(pCursor->pRun)->member.para.nCharOfs + + pCursor->pRun->member.run.nCharOfs + pCursor->nOffset; +} + +int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol) +{ + ME_DisplayItem *p = editor->pBuffer->pFirst->member.para.next_para; + int rx = 0; + + if (is_eol) + *is_eol = 0; + + while(p != editor->pBuffer->pLast) + { + if (p->type == diParagraph) + { + int ry = y - p->member.para.nYPos; + if (ry < 0) + { + result->pRun = ME_FindItemFwd(p, diRun); + result->nOffset = 0; + return 0; + } + if (ry >= p->member.para.nHeight) + { + p = p->member.para.next_para; + continue; + } + p = ME_FindItemFwd(p, diStartRow); + y = ry; + continue; + } + if (p->type == diStartRow) + { + int ry = y - p->member.row.nYPos; + if (ry < 0) + return 0; + if (ry >= p->member.row.nHeight) + { + p = ME_FindItemFwd(p, diStartRowOrParagraphOrEnd); + if (p->type != diStartRow) + return 0; + continue; + } + p = ME_FindItemFwd(p, diRun); + continue; + } + if (p->type == diRun) + { + ME_DisplayItem *pp; + rx = x - p->member.run.pt.x; + if (rx < 0) + rx = 0; + if (rx >= p->member.run.nWidth) /* not this run yet... find next item */ + { + pp = p; + do { + p = p->next; + if (p->type == diRun) + { + rx = x - p->member.run.pt.x; + goto continue_search; + } + if (p->type == diStartRow) + { + p = ME_FindItemFwd(p, diRun); + if (is_eol) + *is_eol = 1; + rx = 0; /* FIXME not sure */ + goto found_here; + } + if (p->type == diParagraph || p->type == diTextEnd) + { + rx = 0; /* FIXME not sure */ + p = pp; + goto found_here; + } + } while(1); + continue; + } + found_here: + if (p->member.run.nFlags & MERF_ENDPARA) + rx = 0; + result->pRun = p; + result->nOffset = ME_CharFromPointCursor(editor, rx, &p->member.run); + if (editor->pCursors[0].nOffset == p->member.run.strText->nLen && rx) + { + result->pRun = ME_FindItemFwd(editor->pCursors[0].pRun, diRun); + result->nOffset = 0; + } + return 1; + } + assert(0); + continue_search: + ; + } + result->pRun = ME_FindItemBack(p, diRun); + result->nOffset = 0; + assert(result->pRun->member.run.nFlags & MERF_ENDPARA); + return 0; +} + +void ME_LButtonDown(ME_TextEditor *editor, int x, int y) +{ + ME_Cursor tmp_cursor; + int is_selection = 0; + + editor->nUDArrowX = -1; + + y += GetScrollPos(editor->hWnd, SB_VERT); + + tmp_cursor = editor->pCursors[0]; + is_selection = ME_IsSelection(editor); + + ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd); + + if (GetKeyState(VK_SHIFT)>=0) + { + editor->pCursors[1] = editor->pCursors[0]; + } + else + { + if (!is_selection) { + editor->pCursors[1] = tmp_cursor; + is_selection = 1; + } + } + HideCaret(editor->hWnd); + ME_MoveCaret(editor); + if (is_selection) + ME_Repaint(editor); + ShowCaret(editor->hWnd); + ME_ClearTempStyle(editor); + ME_SendSelChange(editor); +} + +void ME_MouseMove(ME_TextEditor *editor, int x, int y) +{ + ME_Cursor tmp_cursor; + + y += GetScrollPos(editor->hWnd, SB_VERT); + + tmp_cursor = editor->pCursors[0]; + if (!ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd)) + /* return */; + + if (tmp_cursor.pRun == editor->pCursors[0].pRun && + tmp_cursor.nOffset == editor->pCursors[0].nOffset) + return; + + HideCaret(editor->hWnd); + ME_MoveCaret(editor); + ME_Repaint(editor); + ShowCaret(editor->hWnd); + ME_SendSelChange(editor); +} + +static ME_DisplayItem *ME_FindRunInRow(ME_TextEditor *editor, ME_DisplayItem *pRow, + int x, int *pOffset, int *pbCaretAtEnd) +{ + ME_DisplayItem *pNext, *pLastRun; + pNext = ME_FindItemFwd(pRow, diRunOrStartRow); + assert(pNext->type == diRun); + pLastRun = pNext; + *pbCaretAtEnd = FALSE; + do { + int run_x = pNext->member.run.pt.x; + int width = pNext->member.run.nWidth; + if (x < run_x) + { + if (pOffset) *pOffset = 0; + return pNext; + } + if (x >= run_x && x < run_x+width) + { + int ch = ME_CharFromPointCursor(editor, x-run_x, &pNext->member.run); + ME_String *s = pNext->member.run.strText; + if (ch < s->nLen) { + if (pOffset) + *pOffset = ch; + return pNext; + } + } + pLastRun = pNext; + pNext = ME_FindItemFwd(pNext, diRunOrStartRow); + } while(pNext && pNext->type == diRun); + + if ((pLastRun->member.run.nFlags & MERF_ENDPARA) == 0) + { + pNext = ME_FindItemFwd(pNext, diRun); + if (pbCaretAtEnd) *pbCaretAtEnd = 1; + if (pOffset) *pOffset = 0; + return pNext; + } else { + if (pbCaretAtEnd) *pbCaretAtEnd = 0; + if (pOffset) *pOffset = 0; + return pLastRun; + } +} + +static int ME_GetXForArrow(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRun = pCursor->pRun; + int x; + + if (editor->nUDArrowX != -1) + x = editor->nUDArrowX; + else { + if (editor->bCaretAtEnd) + { + pRun = ME_FindItemBack(pRun, diRun); + assert(pRun); + x = pRun->member.run.pt.x + pRun->member.run.nWidth; + } + else { + x = pRun->member.run.pt.x; + x += ME_PointFromChar(editor, &pRun->member.run, pCursor->nOffset); + } + editor->nUDArrowX = x; + } + return x; +} + +void ME_ArrowUp(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRun = pCursor->pRun; + ME_DisplayItem *pItem, *pItem2; + int x = ME_GetXForArrow(editor, pCursor); + + if (editor->bCaretAtEnd && !pCursor->nOffset) + { + pRun = ME_FindItemBack(pRun, diRun); + if (!pRun) + return; + } + + /* start of this row */ + pItem = ME_FindItemBack(pRun, diStartRow); + assert(pItem); + /* start of the previous row */ + pItem2 = ME_FindItemBack(pItem, diStartRow); + /* no previous row = the first line of the first paragraph */ + if (!pItem2) /* can't go up - don't go BOL (as in MS richedit) */ + return; + /* FIXME + ME_WrapTextParagraph(editor, ME_FindItemBack(pItem2, diParagraph)); + */ + pCursor->pRun = ME_FindRunInRow(editor, pItem2, x, &pCursor->nOffset, &editor->bCaretAtEnd); +} + +void ME_ArrowDown(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRun = pCursor->pRun; + ME_DisplayItem *pItem; + int x = ME_GetXForArrow(editor, pCursor); + if (!pCursor->nOffset && editor->bCaretAtEnd) + { + pRun = ME_FindItemBack(pRun, diRun); +/* x = pRun->member.run.pt.x + pRun->member.run.nWidth; */ + } + /* start of the next row */ + pItem = ME_FindItemFwd(pRun, diStartRow); + /* FIXME If diParagraph is before diStartRow, wrap the next paragraph? + */ + if (!pItem) + { + /* next row not found - ignore */ + return; + } + pCursor->pRun = ME_FindRunInRow(editor, pItem, x, &pCursor->nOffset, &editor->bCaretAtEnd); + assert(pCursor->pRun); + assert(pCursor->pRun->type == diRun); +} + +void ME_ArrowHome(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow); + if (pRow) { + ME_DisplayItem *pRun; + if (editor->bCaretAtEnd && !pCursor->nOffset) { + pRow = ME_FindItemBack(pRow, diStartRow); + if (!pRow) + return; + } + pRun = ME_FindItemFwd(pRow, diRun); + if (pRun) { + pCursor->pRun = pRun; + pCursor->nOffset = 0; + } + } + editor->bCaretAtEnd = FALSE; +} + +void ME_ArrowCtrlHome(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diTextStart); + if (pRow) { + ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun); + if (pRun) { + pCursor->pRun = pRun; + pCursor->nOffset = 0; + } + } +} + +void ME_ArrowEnd(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *pRow; + + if (editor->bCaretAtEnd && !pCursor->nOffset) + return; + + pRow = ME_FindItemFwd(pCursor->pRun, diStartRowOrParagraphOrEnd); + assert(pRow); + if (pRow->type == diStartRow) { + /* FIXME WTF was I thinking about here ? */ + ME_DisplayItem *pRun = ME_FindItemFwd(pRow, diRun); + assert(pRun); + pCursor->pRun = pRun; + pCursor->nOffset = 0; + editor->bCaretAtEnd = 1; + return; + } + pCursor->pRun = ME_FindItemBack(pRow, diRun); + assert(pCursor->pRun && pCursor->pRun->member.run.nFlags & MERF_ENDPARA); + pCursor->nOffset = 0; + editor->bCaretAtEnd = FALSE; +} + +void ME_ArrowCtrlEnd(ME_TextEditor *editor, ME_Cursor *pCursor) +{ + ME_DisplayItem *p = ME_FindItemFwd(pCursor->pRun, diTextEnd); + assert(p); + p = ME_FindItemBack(p, diRun); + assert(p); + assert(p->member.run.nFlags & MERF_ENDPARA); + pCursor->pRun = p; + pCursor->nOffset = 0; + editor->bCaretAtEnd = FALSE; +} + +BOOL ME_IsSelection(ME_TextEditor *editor) +{ + return memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor))!=0; +} + +int ME_GetSelCursor(ME_TextEditor *editor, int dir) +{ + int cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1); + + if (cdir*dir>0) + return 0; + else + return 1; +} + +BOOL ME_CancelSelection(ME_TextEditor *editor, int dir) +{ + int cdir; + + if (GetKeyState(VK_SHIFT)<0) + return FALSE; + if (!memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor))) + return FALSE; + + cdir = ME_GetCursorOfs(editor, 0) - ME_GetCursorOfs(editor, 1); + + if (cdir*dir>0) + editor->pCursors[1] = editor->pCursors[0]; + else + editor->pCursors[0] = editor->pCursors[1]; + /* FIXME optimize */ + ME_MarkAllForWrapping(editor); + ME_Repaint(editor); + return TRUE; +} + +void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor) +{ + ME_Cursor old_anchor = editor->pCursors[1]; + BOOL bRedraw = FALSE; + bRedraw = memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor)); + + if (bRedraw) + { + /* FIXME optimize */ + ME_MarkAllForWrapping(editor); + } + + if (GetKeyState(VK_SHIFT)>=0) /* cancelling selection */ + { + /* any selection was present ? if so, it's no more, repaint ! */ + editor->pCursors[1] = editor->pCursors[0]; + if (memcmp(pTempCursor, &old_anchor, sizeof(ME_Cursor))) { + ME_Repaint(editor); + return; + } + return; + } + else + { + if (!memcmp(pTempCursor, &editor->pCursors[1], sizeof(ME_Cursor))) /* starting selection */ + { + editor->pCursors[1] = *pTempCursor; + } + } + + ME_Repaint(editor); +} + +void ME_DeleteSelection(ME_TextEditor *editor) +{ + int from, to; + ME_GetSelection(editor, &from, &to); + ME_DeleteTextAtCursor(editor, ME_GetSelCursor(editor,-1), to-from); +} + +void ME_SendSelChange(ME_TextEditor *editor) +{ + SELCHANGE sc; + if (!(editor->nEventMask & ENM_SELCHANGE)) + return; + sc.nmhdr.hwndFrom = editor->hWnd; + sc.nmhdr.idFrom = GetWindowLongW(editor->hWnd, GWLP_ID); + sc.nmhdr.code = EN_SELCHANGE; + SendMessageW(editor->hWnd, EM_EXGETSEL, 0, (LPARAM)&sc.chrg); + sc.seltyp = SEL_EMPTY; + if (sc.chrg.cpMin != sc.chrg.cpMax) + sc.seltyp |= SEL_TEXT; + if (sc.chrg.cpMin < sc.chrg.cpMax+1) /* wth were RICHEDIT authors thinking ? */ + sc.seltyp |= SEL_MULTICHAR; + SendMessageW(GetParent(editor->hWnd), WM_NOTIFY, sc.nmhdr.idFrom, (LPARAM)&sc); +} + +BOOL ME_ArrowKey(ME_TextEditor *editor, int nVKey, int nCtrl) +{ + int nCursor = 0; + ME_Cursor *p = &editor->pCursors[nCursor]; + ME_Cursor tmp_curs = *p; + + switch(nVKey) { + case VK_UP: + ME_ArrowUp(editor, p); + ME_ClearTempStyle(editor); + ME_RepaintSelection(editor, &tmp_curs); + ME_SendSelChange(editor); + return TRUE; + case VK_DOWN: + ME_ArrowDown(editor, p); + ME_ClearTempStyle(editor); + ME_RepaintSelection(editor, &tmp_curs); + ME_SendSelChange(editor); + return TRUE; + } + + editor->nUDArrowX = -1; + switch(nVKey) { + case VK_BACK: { /* FIXME backspace and delete aren't the same, they act different wrt paragraph style of the merged paragraph */ + if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) + return FALSE; + if (ME_IsSelection(editor)) + { + editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */ + ME_DeleteSelection(editor); + ME_UpdateRepaint(editor); + return TRUE; + } + if (ME_ArrowLeft(editor, p)) { + editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */ + ME_MoveCaret(editor); + ME_DeleteTextAtCursor(editor, nCursor, 1); + ME_UpdateRepaint(editor); + } + return TRUE; + } + case VK_DELETE: { + if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) + return FALSE; + /* editor->bCaretAtEnd = 0; FIXME or maybe not */ + if (ME_IsSelection(editor)) + { + ME_DeleteSelection(editor); + ME_UpdateRepaint(editor); + return TRUE; + } + ME_DeleteTextAtCursor(editor, nCursor, 1); + ME_UpdateRepaint(editor); + return TRUE; + } + case VK_HOME: { + if (GetKeyState(VK_CONTROL)<0) + ME_ArrowCtrlHome(editor, p); + else + ME_ArrowHome(editor, p); + editor->bCaretAtEnd = 0; + ME_ClearTempStyle(editor); + ME_RepaintSelection(editor, &tmp_curs); + ME_SendSelChange(editor); + return TRUE; + } + case VK_END: + if (GetKeyState(VK_CONTROL)<0) + ME_ArrowCtrlEnd(editor, p); + else + ME_ArrowEnd(editor, p); + ME_ClearTempStyle(editor); + ME_RepaintSelection(editor, &tmp_curs); + ME_SendSelChange(editor); + return TRUE; + case VK_LEFT: + editor->bCaretAtEnd = 0; + if (ME_CancelSelection(editor, -1)) + return TRUE; + ME_ArrowLeft(editor, p); + ME_RepaintSelection(editor, &tmp_curs); + ME_ClearTempStyle(editor); + ME_SendSelChange(editor); + return TRUE; + case VK_RIGHT: + editor->bCaretAtEnd = 0; + if (ME_CancelSelection(editor, +1)) + return TRUE; + ME_ArrowRight(editor, p); + ME_RepaintSelection(editor, &tmp_curs); + ME_ClearTempStyle(editor); + ME_SendSelChange(editor); + return TRUE; + } + return FALSE; +} diff --git a/dlls/riched20/context.c b/dlls/riched20/context.c new file mode 100644 index 00000000000..62b02590608 --- /dev/null +++ b/dlls/riched20/context.c @@ -0,0 +1,37 @@ +/* + * RichEdit - Operation context functions + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC) +{ + c->nSequence = editor->nSequence++; + c->hDC = hDC; + c->editor = editor; + c->pt.x = 0; + c->pt.y = 0; + c->hbrMargin = CreateSolidBrush(RGB(224,224,224)); + GetClientRect(editor->hWnd, &c->rcView); +} + +void ME_DestroyContext(ME_Context *c) +{ + DeleteObject(c->hbrMargin); +} diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c new file mode 100644 index 00000000000..5ac4b186490 --- /dev/null +++ b/dlls/riched20/editor.c @@ -0,0 +1,998 @@ +/* + * RichEdit - functions dealing with editor object + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + API implementation status: + + Messages (ANSI versions not done yet) + - EM_AUTOURLDETECT 2.0 + - EM_CANPASTE + + EM_CANREDO 2.0 + + EM_CANUNDO + - EM_CHARFROMPOS + - EM_DISPLAYBAND + + EM_EMPTYUNDOBUFFER + + EM_EXGETSEL + - EM_EXLIMITTEXT + - EM_EXLINEFROMCHAR + + EM_EXSETSEL + - EM_FINDTEXT + - EM_FINDTEXTEX + - EM_FINDWORDBREAK + - EM_FMTLINES + - EM_FORMATRANGE + - EM_GETCHARFORMAT (partly done) + + EM_GETEVENTMASK + - EM_GETFIRSTVISIBLELINE + - EM_GETIMECOLOR 1.0asian + - EM_GETIMECOMPMODE 2.0 + - EM_GETIMEOPTIONS 1.0asian + - EM_GETIMESTATUS + - EM_GETLANGOPTIONS 2.0 + - EM_GETLIMITTEXT + - EM_GETLINE + - EM_GETLINECOUNT returns number of rows, not of paragraphs + + EM_GETMODIFY + - EM_GETOLEINTERFACE + - EM_GETOPTIONS + + EM_GETPARAFORMAT + - EM_GETPUNCTUATION 1.0asian + - EM_GETRECT + - EM_GETREDONAME 2.0 + + EM_GETSEL + + EM_GETSELTEXT (ANSI&Unicode) +! - EM_GETTHUMB + - EM_GETTEXTMODE 2.0 +? + EM_GETTEXTRANGE (ANSI&Unicode) + - EM_GETUNDONAME + - EM_GETWORDBREAKPROC + - EM_GETWORDBREAKPROCEX + - EM_GETWORDWRAPMODE 1.0asian + - EM_HIDESELECTION + - EM_LIMITTEXT + - EM_LINEFROMCHAR + - EM_LINEINDEX + - EM_LINELENGTH + - EM_LINESCROLL + - EM_PASTESPECIAL + - EM_POSFROMCHARS + - EM_REDO 2.0 + - EM_REQUESTRESIZE + + EM_REPLACESEL (proper style?) ANSI&Unicode + - EM_SCROLL + - EM_SCROLLCARET + - EM_SELECTIONTYPE + + EM_SETBKGNDCOLOR + - EM_SETCHARFORMAT (partly done, no ANSI) + + EM_SETEVENTMASK (few notifications supported) + - EM_SETIMECOLOR 1.0asian + - EM_SETIMEOPTIONS 1.0asian + - EM_SETLANGOPTIONS 2.0 + - EM_SETLIMITTEXT + + EM_SETMODIFY (not sure if implementation is correct) + - EM_SETOLECALLBACK + - EM_SETOPTIONS + + EM_SETPARAFORMAT + - EM_SETPUNCTUATION 1.0asian + + EM_SETREADONLY no beep on modification attempt + - EM_SETRECT + - EM_SETRECTNP (EM_SETRECT without repainting) - not supported in RICHEDIT + + EM_SETSEL + - EM_SETTARGETDEVICE + - EM_SETTEXTMODE 2.0 + - EM_SETUNDOLIMIT 2.0 + - EM_SETWORDBREAKPROC + - EM_SETWORDBREAKPROCEX + - EM_SETWORDWRAPMODE 1.0asian + - EM_STOPGROUPTYPING 2.0 + - EM_STREAMIN + - EM_STREAMOUT + - EM_UNDO + + WM_CHAR + + WM_CLEAR + - WM_COPY (lame implementation, no RTF support) + - WM_CUT (lame implementation, no RTF support) + + WM_GETDLGCODE (the current implementation is incomplete) + + WM_GETTEXT (ANSI&Unicode) + + WM_GETTEXTLENGTH (ANSI version sucks) + - WM_PASTE + - WM_SETFONT + + WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode + - WM_STYLECHANGING + - WM_STYLECHANGED (things like read-only flag) + - WM_UNICHAR + + Notifications + + * EN_CHANGE (sent from the wrong place) + - EN_CORRECTTEXT + - EN_DROPFILES + - EN_ERRSPACE + - EN_HSCROLL + - EN_IMECHANGE + + EN_KILLFOCUS + - EN_LINK + - EN_MAXTEXT + - EN_MSGFILTER + - EN_OLEOPFAILED + - EN_PROTECTED + - EN_REQUESTRESIZE + - EN_SAVECLIPBOARD + + EN_SELCHANGE + + EN_SETFOCUS + - EN_STOPNOUNDO + * EN_UPDATE (sent from the wrong place) + - EN_VSCROLL + + Styles + + - ES_AUTOHSCROLL + - ES_AUTOVSCROLL + - ES_CENTER + - ES_DISABLENOSCROLL (scrollbar is always visible) + - ES_EX_NOCALLOLEINIT + - ES_LEFT + - ES_MULTILINE (currently single line controls aren't supported) + - ES_NOIME + - ES_READONLY (I'm not sure if beeping is the proper behaviour) + - ES_RIGHT + - ES_SAVESEL + - ES_SELFIME + - ES_SUNKEN + - ES_VERTICAL + - ES_WANTRETURN (don't know how to do WM_GETDLGCODE part) + - WS_SETFONT + - WS_HSCROLL + - WS_VSCROLL +*/ + +/* + * RICHED20 TODO (incomplete): + * + * - font caching + * - add remaining CHARFORMAT/PARAFORMAT fields + * - right/center align should strip spaces from the beginning + * - more advanced navigation (Ctrl-arrows, PageUp/PageDn) + * - tabs + * - pictures (not just smiling faces that lack API support ;-) ) + * - OLE objects + * - calculate heights of pictures (half-done) + * - EM_STREAMIN/EM_STREAMOUT + * - horizontal scrolling (not even started) + * - fix scrollbars and refresh (it sucks bigtime) + * - hysteresis during wrapping (related to scrollbars appearing/disappearing) + * - should remember maximum row width for wrap hysteresis + * - find/replace + * - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible) + * - italic cursor with italic fonts + * - IME + * - most notifications aren't sent at all (the most important ones are) + * - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?) + * - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK) + * - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close) + * - bugs in end-of-text handling (the gray bar) could get me in jail ;-) + * - determination of row size + * - end-of-paragraph marks should be of reasonable size + * + * Bugs that are probably fixed, but not so easy to verify: + * - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now) + * - undo for ME_JoinParagraphs doesn't store paragraph format ? (it does) + * - check/fix artificial EOL logic (bCursorAtEnd, hardly logical) + * - caret shouldn't be displayed when selection isn't empty + * - check refcounting in style management functions (looks perfect now, but no bugs is suspicious) + * - undo for setting default format (done, might be buggy) + * - styles might be not released properly (looks like they work like charm, but who knows? + * + */ + +#include "editor.h" +#include +#include +#include +#define NO_SHLWAPI_STREAM +#include + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +int me_debug = 0; +HANDLE me_heap = NULL; + +void DoWrap(ME_TextEditor *editor) { + HDC hDC = GetDC(editor->hWnd); + ME_DisplayItem *item; + ME_Context c; + HWND hWnd = editor->hWnd; + int yLength = editor->nTotalLength; + int nSelFrom, nSelTo; + int nMinSel, nMaxSel; + + ME_GetSelection(editor, &nSelFrom, &nSelTo); + + nMinSel = nSelFrom < editor->nOldSelFrom ? nSelFrom : editor->nOldSelFrom; + nMaxSel = nSelTo > editor->nOldSelTo ? nSelTo : editor->nOldSelTo; + + ME_InitContext(&c, editor, hDC); + c.pt.x = 0; + c.pt.y = 0; + item = editor->pBuffer->pFirst->next; + while(item != editor->pBuffer->pLast) { + int para_from, para_to; + BOOL bRedraw = FALSE; + + para_from = item->member.para.nCharOfs; + para_to = item->member.para.next_para->member.para.nCharOfs; + + if (para_from <= nMaxSel && para_to >= nMinSel && nMinSel != nMaxSel) + bRedraw = TRUE; + + assert(item->type == diParagraph); + if (!(item->member.para.nFlags & MEPF_WRAPPED) + || (item->member.para.nYPos != c.pt.y)) + bRedraw = TRUE; + item->member.para.nYPos = c.pt.y; + + ME_WrapTextParagraph(&c, item); + + if (bRedraw) { + item->member.para.nFlags |= MEPF_REDRAW; + } + c.pt.y = item->member.para.nYPos + item->member.para.nHeight; + item = item->member.para.next_para; + } + editor->sizeWindow.cx = c.rcView.right-c.rcView.left; + editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top; + editor->nTotalLength = c.pt.y-c.rcView.top; + + ME_UpdateScrollBar(editor, -1); + ME_EnsureVisible(editor, editor->pCursors[0].pRun); + + /* FIXME this should be marked for update too somehow, so that painting happens in ME_PaintContent */ + if (yLength != c.pt.y-c.rcView.top) { + RECT rc; + rc.left = c.rcView.left; + rc.right = c.rcView.right; + rc.top = c.pt.y; + rc.bottom = c.rcView.bottom; + InvalidateRect(editor->hWnd, &rc, FALSE); + UpdateWindow(editor->hWnd); + } + + editor->nOldSelFrom = nSelFrom; + editor->nOldSelTo = nSelTo; + /* PatBlt(hDC, 0, c.pt.y, c.rcView.right, c.rcView.bottom, BLACKNESS);*/ + + ME_DestroyContext(&c); + ReleaseDC(hWnd, hDC); +} + +ME_TextBuffer *ME_MakeText() { + + ME_TextBuffer *buf = ALLOC_OBJ(ME_TextBuffer); + + ME_DisplayItem *p1 = ME_MakeDI(diTextStart); + ME_DisplayItem *p2 = ME_MakeDI(diTextEnd); + + p1->prev = NULL; + p1->next = p2; + p2->prev = p1; + p2->next = NULL; + p1->member.para.next_para = p2; + p2->member.para.prev_para = p1; + p2->member.para.nCharOfs = 0; + + buf->pFirst = p1; + buf->pLast = p2; + buf->pCharStyle = NULL; + + return buf; +} + +ME_TextEditor *ME_MakeEditor(HWND hWnd) { + ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor); + HDC hDC; + ed->hWnd = hWnd; + ed->pBuffer = ME_MakeText(); + hDC = GetDC(hWnd); + ME_MakeFirstParagraph(hDC, ed->pBuffer); + ReleaseDC(hWnd, hDC); + ed->bCaretShown = FALSE; + ed->nCursors = 3; + ed->pCursors = ALLOC_N_OBJ(ME_Cursor, ed->nCursors); + ed->pCursors[0].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun); + ed->pCursors[0].nOffset = 0; + ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun); + ed->pCursors[1].nOffset = 0; + ed->nTotalLength = 0; + ed->nScrollPos = 0; + ed->nUDArrowX = -1; + ed->nSequence = 0; + ed->rgbBackColor = -1; + ed->bCaretAtEnd = FALSE; + ed->nEventMask = 0; + ed->nModifyStep = 0; + ed->pUndoStack = ed->pRedoStack = NULL; + ed->nUndoMode = umAddToUndo; + ed->nParagraphs = 1; + ME_CheckCharOffsets(ed); + return ed; +} + +void ME_DestroyEditor(ME_TextEditor *editor) +{ + ME_DisplayItem *pFirst = editor->pBuffer->pFirst; + ME_DisplayItem *p = pFirst, *pNext = NULL; + + ME_ClearTempStyle(editor); + ME_EmptyUndoStack(editor); + while(p) { + pNext = p->next; + ME_DestroyDisplayItem(p); + p = pNext; + } + ME_ReleaseStyle(editor->pBuffer->pDefaultStyle); + + FREE_OBJ(editor); +} + +#define UNSUPPORTED_MSG(e) \ + case e: \ + FIXME(#e ": stub\n"); \ + return DefWindowProcW(hWnd, msg, wParam, lParam); + +/****************************************************************** + * RichEditANSIWndProc (RICHED20.10) + */ +LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + HDC hDC; + PAINTSTRUCT ps; + SCROLLINFO si; + ME_TextEditor *editor = (ME_TextEditor *)GetWindowLongW(hWnd, 0); + switch(msg) { + + UNSUPPORTED_MSG(EM_AUTOURLDETECT) + UNSUPPORTED_MSG(EM_CANPASTE) + UNSUPPORTED_MSG(EM_CHARFROMPOS) + UNSUPPORTED_MSG(EM_DISPLAYBAND) + UNSUPPORTED_MSG(EM_EXLIMITTEXT) + UNSUPPORTED_MSG(EM_EXLINEFROMCHAR) + UNSUPPORTED_MSG(EM_FINDTEXT) + UNSUPPORTED_MSG(EM_FINDTEXTEX) + UNSUPPORTED_MSG(EM_FINDWORDBREAK) + UNSUPPORTED_MSG(EM_FMTLINES) + UNSUPPORTED_MSG(EM_FORMATRANGE) + UNSUPPORTED_MSG(EM_GETFIRSTVISIBLELINE) + UNSUPPORTED_MSG(EM_GETIMECOMPMODE) + /* UNSUPPORTED_MSG(EM_GETIMESTATUS) missing in Wine headers */ + UNSUPPORTED_MSG(EM_GETLANGOPTIONS) + UNSUPPORTED_MSG(EM_GETLIMITTEXT) + UNSUPPORTED_MSG(EM_GETLINE) + UNSUPPORTED_MSG(EM_GETLINECOUNT) + /* UNSUPPORTED_MSG(EM_GETOLEINTERFACE) separate stub */ + UNSUPPORTED_MSG(EM_GETOPTIONS) + UNSUPPORTED_MSG(EM_GETRECT) + UNSUPPORTED_MSG(EM_GETREDONAME) + UNSUPPORTED_MSG(EM_GETTEXTMODE) + UNSUPPORTED_MSG(EM_GETUNDONAME) + UNSUPPORTED_MSG(EM_GETWORDBREAKPROC) + UNSUPPORTED_MSG(EM_GETWORDBREAKPROCEX) + UNSUPPORTED_MSG(EM_HIDESELECTION) + UNSUPPORTED_MSG(EM_LIMITTEXT) /* also known as EM_SETLIMITTEXT */ + UNSUPPORTED_MSG(EM_LINEFROMCHAR) + UNSUPPORTED_MSG(EM_LINEINDEX) + UNSUPPORTED_MSG(EM_LINELENGTH) + UNSUPPORTED_MSG(EM_LINESCROLL) + UNSUPPORTED_MSG(EM_PASTESPECIAL) +/* UNSUPPORTED_MSG(EM_POSFROMCHARS) missing in Wine headers */ + UNSUPPORTED_MSG(EM_REQUESTRESIZE) + UNSUPPORTED_MSG(EM_SCROLL) + UNSUPPORTED_MSG(EM_SCROLLCARET) + UNSUPPORTED_MSG(EM_SELECTIONTYPE) + UNSUPPORTED_MSG(EM_SETLANGOPTIONS) + UNSUPPORTED_MSG(EM_SETOLECALLBACK) + UNSUPPORTED_MSG(EM_SETOPTIONS) + UNSUPPORTED_MSG(EM_SETRECT) + UNSUPPORTED_MSG(EM_SETRECTNP) + UNSUPPORTED_MSG(EM_SETTARGETDEVICE) + UNSUPPORTED_MSG(EM_SETTEXTMODE) + UNSUPPORTED_MSG(EM_SETUNDOLIMIT) + UNSUPPORTED_MSG(EM_SETWORDBREAKPROC) + UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX) + UNSUPPORTED_MSG(EM_STREAMIN) + UNSUPPORTED_MSG(EM_STREAMOUT) + UNSUPPORTED_MSG(WM_SETFONT) + UNSUPPORTED_MSG(WM_PASTE) + UNSUPPORTED_MSG(WM_STYLECHANGING) + UNSUPPORTED_MSG(WM_STYLECHANGED) +/* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */ + + + case WM_GETDLGCODE: + { + UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS; + if (GetWindowLongW(hWnd, GWL_STYLE)&ES_WANTRETURN) + code |= 0; /* FIXME what can we do here ? ask for messages and censor them ? */ + return code; + } + case WM_NCCREATE: + { + CREATESTRUCTW *pcs = (CREATESTRUCTW *)lParam; + editor = ME_MakeEditor(hWnd); + SetWindowLongW(hWnd, 0, (long)editor); + pcs = 0; /* ignore */ + return TRUE; + } + case EM_EMPTYUNDOBUFFER: + ME_EmptyUndoStack(editor); + return 0; + case EM_GETSEL: + { + ME_GetSelection(editor, (int *)wParam, (int *)lParam); + if (!((wParam|lParam) & 0xFFFF0000)) + return (lParam<<16)|wParam; + return -1; + } + case EM_EXGETSEL: + { + CHARRANGE *pRange = (CHARRANGE *)lParam; + ME_GetSelection(editor, (int *)&pRange->cpMin, (int *)&pRange->cpMax); + return 0; + } + case EM_CANUNDO: + return editor->pUndoStack != NULL; + case EM_CANREDO: + return editor->pRedoStack != NULL; + case EM_UNDO: + ME_Undo(editor); + return 0; + case EM_REDO: + ME_Redo(editor); + return 0; + case EM_SETSEL: + { + ME_SetSelection(editor, wParam, lParam); + ME_Repaint(editor); + ME_SendSelChange(editor); + return 0; + } + case EM_EXSETSEL: + { + CHARRANGE *pRange = (CHARRANGE *)lParam; + ME_SetSelection(editor, pRange->cpMin, pRange->cpMax); + /* FIXME optimize */ + ME_Repaint(editor); + ME_SendSelChange(editor); + return 0; + } + case EM_SETBKGNDCOLOR: + { + LRESULT lColor = ME_GetBackColor(editor); + if (wParam) + editor->rgbBackColor = -1; + else + editor->rgbBackColor = lParam; + InvalidateRect(hWnd, NULL, TRUE); + UpdateWindow(hWnd); + return lColor; + } + case EM_GETMODIFY: + return editor->nModifyStep == 0 ? 0 : 1; + case EM_SETMODIFY: + { + if (wParam) + editor->nModifyStep = 0x80000000; + else + editor->nModifyStep = 0; + + return 0; + } + case EM_SETREADONLY: + { + long nStyle = GetWindowLongW(hWnd, GWL_STYLE); + if (wParam) + nStyle |= ES_READONLY; + else + nStyle &= ~ES_READONLY; + SetWindowLongW(hWnd, GWL_STYLE, nStyle); + ME_Repaint(editor); + return 0; + } + case EM_SETEVENTMASK: + editor->nEventMask = lParam; + return 0; + case EM_GETEVENTMASK: + return editor->nEventMask; + case EM_SETCHARFORMAT: + { + CHARFORMAT2W buf, *p; + p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam); + if (!wParam) + ME_SetDefaultCharFormat(editor, p); + else if (wParam == (SCF_WORD | SCF_SELECTION)) + FIXME("word selection not supported\n"); + else if (wParam == SCF_ALL) + ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p); + else + ME_SetSelectionCharFormat(editor, p); + ME_CommitUndo(editor); + ME_UpdateRepaint(editor); + return 0; + } + case EM_GETCHARFORMAT: + { + CHARFORMAT2W tmp; + tmp.cbSize = sizeof(tmp); + if (!wParam) + ME_GetDefaultCharFormat(editor, &tmp); + else + ME_GetSelectionCharFormat(editor, &tmp); + ME_CopyToCFAny((CHARFORMAT2W *)lParam, &tmp); + return 0; + } + case EM_SETPARAFORMAT: + ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); + ME_CommitUndo(editor); + return 0; + case EM_GETPARAFORMAT: + ME_GetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam); + return 0; + case WM_CLEAR: + { + int from, to; + ME_GetSelection(editor, &from, &to); + ME_InternalDeleteText(editor, from, to-from); + ME_CommitUndo(editor); + ME_UpdateRepaint(editor); + return 0; + } + case EM_REPLACESEL: + { + int from, to; + ME_Cursor c; + ME_Style *style; + LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam); + size_t len = lstrlenW(wszText); + + ME_GetSelection(editor, &from, &to); + ME_CursorFromCharOfs(editor, from, &c); + if (from != to) { + style = c.pRun->member.run.style; + ME_AddRefStyle(style); /* ME_GetInsertStyle has already done that */ + } + else + style = ME_GetInsertStyle(editor, 0); + ME_InternalDeleteText(editor, from, to-from); + ME_InsertTextFromCursor(editor, 0, wszText, len, style); + ME_ReleaseStyle(style); + ME_EndToUnicode(hWnd, wszText); + /* drop temporary style if line end */ + /* FIXME question: does abc\n mean: put abc, clear temp style, put \n? (would require a change) */ + if (len>0 && wszText[len-1] == '\n') + ME_ClearTempStyle(editor); + + ME_CommitUndo(editor); + if (!wParam) + ME_EmptyUndoStack(editor); + ME_UpdateRepaint(editor); + return 0; + } + case WM_SETTEXT: + { + LPWSTR wszText = ME_ToUnicode(hWnd, (void *)lParam); + ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor)); + /* uses default style! */ + ME_InsertTextFromCursor(editor, 0, wszText, -1, editor->pBuffer->pDefaultStyle); + ME_EndToUnicode(hWnd, wszText); + ME_CommitUndo(editor); + ME_EmptyUndoStack(editor); + ME_UpdateRepaint(editor); + return 0; + } + case WM_CUT: + case WM_COPY: + { + int from, to, pars; + WCHAR *data; + HANDLE hData; + + if (!OpenClipboard(hWnd)) + return 0; + + EmptyClipboard(); + ME_GetSelection(editor, &from, &to); + pars = ME_CountParagraphsBetween(editor, from, to); + hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(WCHAR)*(to-from+pars+1)); + data = (WCHAR *)GlobalLock(hData); + ME_GetTextW(editor, data, from, to-from, TRUE); + GlobalUnlock(hData); + SetClipboardData(CF_UNICODETEXT, hData); + CloseClipboard(); + if (msg == WM_CUT) + { + ME_InternalDeleteText(editor, from, to-from); + ME_CommitUndo(editor); + ME_UpdateRepaint(editor); + } + return 0; + } + case WM_GETTEXTLENGTH: + return ME_GetTextLength(editor); + case WM_GETTEXT: + { + TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */ + tr.chrg.cpMin = 0; + tr.chrg.cpMax = wParam-1; + tr.lpstrText = (WCHAR *)lParam; + return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + } + case EM_GETSELTEXT: + { + int from, to; + TEXTRANGEW tr; /* W and A differ only by rng->lpstrText */ + ME_GetSelection(editor, &from, &to); + tr.chrg.cpMin = from; + tr.chrg.cpMax = to; + tr.lpstrText = (WCHAR *)lParam; + return RichEditANSIWndProc(hWnd, EM_GETTEXTRANGE, 0, (LPARAM)&tr); + } + case EM_GETTEXTRANGE: + { + TEXTRANGEW *rng = (TEXTRANGEW *)lParam; + if (IsWindowUnicode(hWnd)) + return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE); + else + { + int nLen = rng->chrg.cpMax-rng->chrg.cpMin; + WCHAR *p = ALLOC_N_OBJ(WCHAR, nLen+1); + int nChars = ME_GetTextW(editor, p, rng->chrg.cpMin, nLen, FALSE); + /* FIXME this is a potential security hole (buffer overrun) + if you know more about wchar->mbyte conversion please explain + */ + WideCharToMultiByte(CP_ACP, 0, p, nChars+1, (char *)rng->lpstrText, nLen+1, NULL, NULL); + FREE_OBJ(p); + return nChars; + } + return ME_GetTextW(editor, rng->lpstrText, rng->chrg.cpMin, rng->chrg.cpMax-rng->chrg.cpMin, FALSE); + } + case WM_CREATE: + ME_CommitUndo(editor); +/* ME_InsertTextFromCursor(editor, 0, (WCHAR *)L"x", 1, editor->pBuffer->pDefaultStyle); */ + DoWrap(editor); + ME_MoveCaret(editor); + return 0; + case WM_DESTROY: + ME_DestroyEditor(editor); + SetWindowLongW(hWnd, 0, 0); + PostQuitMessage(0); + break; + case WM_LBUTTONDOWN: + SetFocus(hWnd); + ME_LButtonDown(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)); + SetCapture(hWnd); + break; + case WM_MOUSEMOVE: + if (GetCapture() == hWnd) + ME_MouseMove(editor, (short)LOWORD(lParam), (short)HIWORD(lParam)); + break; + case WM_LBUTTONUP: + if (GetCapture() == hWnd) + ReleaseCapture(); + break; + case WM_PAINT: + hDC = BeginPaint(hWnd, &ps); + ME_PaintContent(editor, hDC, FALSE); + EndPaint(hWnd, &ps); + break; + case WM_SETFOCUS: + ME_ShowCaret(editor); + ME_SendOldNotify(editor, EN_SETFOCUS); + return 0; + case WM_KILLFOCUS: + ME_HideCaret(editor); + ME_SendOldNotify(editor, EN_KILLFOCUS); + return 0; + case WM_ERASEBKGND: + { + HDC hDC = (HDC)wParam; + RECT rc; + COLORREF rgbBG = ME_GetBackColor(editor); + if (GetUpdateRect(hWnd,&rc,TRUE)) + { + HBRUSH hbr = CreateSolidBrush(rgbBG); + FillRect(hDC, &rc, hbr); + DeleteObject(hbr); + } + return 1; + } + case WM_COMMAND: + TRACE("editor wnd command = %d\n", LOWORD(wParam)); + return 0; + case WM_KEYDOWN: + if (ME_ArrowKey(editor, LOWORD(wParam), GetKeyState(VK_CONTROL)<0)) { + ME_CommitUndo(editor); + ME_EnsureVisible(editor, editor->pCursors[0].pRun); + HideCaret(hWnd); + ME_MoveCaret(editor); + ShowCaret(hWnd); + return 0; + } + if (GetKeyState(VK_CONTROL)<0) + { + if (LOWORD(wParam)=='W') + { + CHARFORMAT2W chf; + char buf[2048]; + ME_GetSelectionCharFormat(editor, &chf); + ME_DumpStyleToBuf(&chf, buf); + MessageBoxA(NULL, buf, "Style dump", MB_OK); + } + if (LOWORD(wParam)=='Q') + { + ME_CheckCharOffsets(editor); + } + } + goto do_default; + case WM_CHAR: + { + WCHAR wstr; + if (GetWindowLongW(editor->hWnd, GWL_STYLE) & ES_READONLY) { + MessageBeep(MB_ICONERROR); + return 0; /* FIXME really 0 ? */ + } + wstr = LOWORD(wParam); + if (((unsigned)wstr)>=' ' || wstr=='\r') { + /* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */ + ME_Style *style = ME_GetInsertStyle(editor, 0); + ME_SaveTempStyle(editor); + ME_InsertTextFromCursor(editor, 0, &wstr, 1, style); + ME_ReleaseStyle(style); + ME_CommitUndo(editor); + ME_UpdateRepaint(editor); + } + return 0; + } + case WM_VSCROLL: + { + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS; + GetScrollInfo(hWnd, SB_VERT, &si); + switch(LOWORD(wParam)) { + case SB_THUMBTRACK: + SetScrollPos(hWnd, SB_VERT, si.nTrackPos, FALSE); + ScrollWindow(hWnd, 0, si.nPos-si.nTrackPos, NULL, NULL); + /* InvalidateRect(hWnd, NULL, TRUE); */ + UpdateWindow(hWnd); + break; + } + break; + } + case WM_SIZE: + { + ME_DisplayItem *tp = editor->pBuffer->pFirst; + while(tp) + { + if (tp->type == diParagraph) + { + tp->member.para.nFlags &= ~MEPF_WRAPPED; + tp = tp->member.para.next_para; + } + else + tp = tp->next; + } + ME_Repaint(editor); + return DefWindowProcW(hWnd, msg, wParam, lParam); + } + case EM_GETOLEINTERFACE: + FIXME("EM_GETOLEINTERFACE: stub\n"); + /* return 0x12345678; to find out if the missing interface is + related to app crash */ + return 0; + default: + do_default: + return DefWindowProcW(hWnd, msg, wParam, lParam); + } + return 0L; +} + +/****************************************************************** + * RichEdit10ANSIWndProc (RICHED20.9) + */ +LRESULT WINAPI RichEdit10ANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + /* FIXME: this is NOT the same as 2.0 version */ + return RichEditANSIWndProc(hWnd, msg, wParam, lParam); +} + +void ME_SendOldNotify(ME_TextEditor *editor, int nCode) +{ + HWND hWnd = editor->hWnd; + SendMessageA(GetParent(hWnd), WM_COMMAND, (nCode<<16)|GetWindowLongW(hWnd, GWLP_ID), (LPARAM)hWnd); +} + +int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to) +{ + ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); + int i = 0; + + while(item && item->member.para.next_para->member.para.nCharOfs <= from) + item = item->member.para.next_para; + if (!item) + return 0; + while(item && item->member.para.next_para->member.para.nCharOfs <= to) { + item = item->member.para.next_para; + i++; + } + return i; +} + +int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF) +{ + ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); + int nWritten = 0; + + while(item && item->member.para.next_para->member.para.nCharOfs <= nStart) + item = ME_FindItemFwd(item, diParagraph); + if (!item) { + *buffer = L'\0'; + return 0; + } + nStart -= item->member.para.nCharOfs; + + do { + item = ME_FindItemFwd(item, diRun); + } while(item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nStart)); + assert(item); + + nStart -= item->member.run.nCharOfs; + + if (nStart) + { + int nLen = ME_StrLen(item->member.run.strText) - nStart; + if (nLen > nChars) + nLen = nChars; + CopyMemory(buffer, item->member.run.strText->szData + nStart, sizeof(WCHAR)*nLen); + nChars -= nLen; + nWritten += nLen; + if (!nChars) + return nWritten; + buffer += nLen; + nStart = 0; + item = ME_FindItemFwd(item, diRun); + } + + while(nChars && item) + { + int nLen = ME_StrLen(item->member.run.strText); + if (nLen > nChars) + nLen = nChars; + + if (item->member.run.nFlags & MERF_ENDPARA) + { + if (bCRLF) { + *buffer++ = '\r'; + nWritten++; + } + *buffer = '\n'; + assert(nLen == 1); + } + else + CopyMemory(buffer, item->member.run.strText->szData, sizeof(WCHAR)*nLen); + nChars -= nLen; + nWritten += nLen; + buffer += nLen; + + if (!nChars) + { + *buffer = L'\0'; + return nWritten; + } + item = ME_FindItemFwd(item, diRun); + } + *buffer = L'\0'; + return nWritten; +} + +static WCHAR wszClassName[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '2', '0', 'W', 0}; +static WCHAR wszClassName50[] = {'R', 'i', 'c', 'h', 'E', 'd', 'i', 't', '5', '0', 'W', 0}; + +void ME_RegisterEditorClass(HINSTANCE hInstance) +{ + BOOL bResult; + WNDCLASSW wcW; + WNDCLASSA wcA; + + wcW.style = CS_HREDRAW | CS_VREDRAW; + wcW.lpfnWndProc = RichEditANSIWndProc; + wcW.cbClsExtra = 0; + wcW.cbWndExtra = 4; + wcW.hInstance = NULL; /* hInstance would register DLL-local class */ + wcW.hIcon = NULL; + wcW.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM)); + wcW.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wcW.lpszMenuName = NULL; + wcW.lpszClassName = wszClassName; + bResult = RegisterClassW(&wcW); + assert(bResult); + wcW.lpszClassName = wszClassName50; + bResult = RegisterClassW(&wcW); + assert(bResult); + + wcA.style = CS_HREDRAW | CS_VREDRAW; + wcA.lpfnWndProc = RichEditANSIWndProc; + wcA.cbClsExtra = 0; + wcA.cbWndExtra = 4; + wcA.hInstance = NULL; /* hInstance would register DLL-local class */ + wcA.hIcon = NULL; + wcA.hCursor = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_IBEAM)); + wcA.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wcA.lpszMenuName = NULL; + wcA.lpszClassName = "RichEdit20A"; + bResult = RegisterClassA(&wcA); + assert(bResult); + wcA.lpszClassName = "RichEdit50A"; + bResult = RegisterClassA(&wcA); + assert(bResult); +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("\n"); + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + me_heap = HeapCreate (0, 0x10000, 0); + ME_RegisterEditorClass(hinstDLL); + break; + + case DLL_PROCESS_DETACH: + UnregisterClassW(wszClassName, hinstDLL); + UnregisterClassA("RichEdit20A", hinstDLL); + HeapDestroy (me_heap); + me_heap = NULL; + break; + } + return TRUE; +} + +/****************************************************************** + * CreateTextServices (RICHED20.4) + * + * FIXME should be ITextHost instead of void* + */ +HRESULT WINAPI CreateTextServices(IUnknown *punkOuter, void *pITextHost, + IUnknown **ppUnk) +{ + FIXME("stub\n"); + /* FIXME should support aggregation */ + if (punkOuter) + return CLASS_E_NOAGGREGATION; + + return E_FAIL; /* E_NOTIMPL isn't allowed by MSDN */ +} + +/****************************************************************** + * REExtendedRegisterClass (RICHED20.8) + * + * FIXME undocumented + */ +void WINAPI REExtendedRegisterClass(void) +{ + FIXME("stub\n"); +} diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h new file mode 100644 index 00000000000..f764ed5f609 --- /dev/null +++ b/dlls/riched20/editor.h @@ -0,0 +1,209 @@ +/* + * RichEdit - prototypes for functions and macro definitions + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editstr.h" + +#define ALLOC_OBJ(type) (type *)HeapAlloc(me_heap, 0, sizeof(type)) +#define ALLOC_N_OBJ(type, count) (type *)HeapAlloc(me_heap, 0, count*sizeof(type)) +#define FREE_OBJ(ptr) HeapFree(me_heap, 0, ptr) + +/* style.c */ +ME_Style *ME_MakeStyle(CHARFORMAT2W *style); +void ME_AddRefStyle(ME_Style *item); +void ME_ReleaseStyle(ME_Style *item); +ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor); +ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style); +void ME_PrepareStyle(ME_Context *c, ME_Style *s); +void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence); +void ME_UnprepareStyle(ME_Style *s); +HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s); +void ME_InitCharFormat2W(CHARFORMAT2W *pFmt); +void ME_SaveTempStyle(ME_TextEditor *editor); +void ME_ClearTempStyle(ME_TextEditor *editor); +void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048]); +void ME_DumpStyle(ME_Style *s); +CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from); +void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from); +CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from); +void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from); +void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc); /* only works with 2W structs */ + +/* list.c */ +void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat); +void ME_Remove(ME_DisplayItem *diWhere); +ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass); +ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass); +ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass); +ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass); +BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass); +ME_DisplayItem *ME_MakeDI(ME_DIType type); +void ME_DestroyDisplayItem(ME_DisplayItem *item); +void ME_DumpDocument(ME_TextBuffer *buffer); +const char *ME_GetDITypeName(ME_DIType type); + +/* string.c */ +int ME_GetOptimalBuffer(int nLen); +ME_String *ME_MakeString(LPCWSTR szText); +ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars); +ME_String *ME_StrDup(ME_String *s); +void ME_DestroyString(ME_String *s); +void ME_AppendString(ME_String *s1, ME_String *s2); +ME_String *ME_ConcatString(ME_String *s1, ME_String *s2); +ME_String *ME_VSplitString(ME_String *orig, int nVPos); +int ME_IsWhitespaces(ME_String *s); +int ME_IsSplitable(ME_String *s); +/* int ME_CalcSkipChars(ME_String *s); */ +int ME_StrLen(ME_String *s); +int ME_StrVLen(ME_String *s); +int ME_FindNonWhitespaceV(ME_String *s, int nVChar); +int ME_FindWhitespaceV(ME_String *s, int nVChar); +int ME_GetCharFwd(ME_String *s, int nPos); /* get char starting from start */ +int ME_GetCharBack(ME_String *s, int nPos); /* get char starting from \0 */ +int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars); +int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars); +int ME_VPosToPos(ME_String *s, int nVPos); +int ME_PosToVPos(ME_String *s, int nPos); +void ME_StrDeleteV(ME_String *s, int nVChar, int nChars); +/* smart helpers for A<->W conversions, they reserve/free memory and call MultiByte<->WideChar functions */ +LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz); +void ME_EndToUnicode(HWND hWnd, LPVOID psz); +LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz); +void ME_EndToAnsi(HWND hWnd, LPVOID psz); + + +/* note: those two really return the first matching offset (starting from EOS)+1 + * in other words, an offset of the first trailing white/black */ +int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar); +int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar); + +/* row.c */ +ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *run, int nRelPos); +ME_DisplayItem *ME_RowStart(ME_DisplayItem *item); +ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item); +void ME_RenumberParagraphs(ME_DisplayItem *item); /* TODO */ + +/* run.c */ +ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags); +/* note: ME_InsertRun inserts a copy of the specified run - so you need to destroy the original */ +ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem); +void ME_CheckCharOffsets(ME_TextEditor *editor); +void ME_PropagateCharOffset(ME_DisplayItem *p, int shift); +void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize); +int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run); +/* this one accounts for 1/2 char tolerance */ +int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run); +int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset); +int ME_GetLastSplittablePlace(ME_Context *c, ME_Run *run); +int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2); +void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p); +ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nChar); +ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nChar); +int ME_FindSplitPoint(ME_Context *c, POINT *pt, ME_Run *run, int desperate); +void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run); +ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *run); +void ME_CalcRunExtent(ME_Context *c, ME_Run *run); +SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen); +void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor); +void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs); +int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs); +void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift); +void ME_SetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt); +void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt); +void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nLen, CHARFORMAT2W *pFmt); +void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt); +void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt); +void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod); + +/* caret.c */ +void ME_SetSelection(ME_TextEditor *editor, int from, int to); +void ME_HideCaret(ME_TextEditor *ed); +void ME_ShowCaret(ME_TextEditor *ed); +void ME_MoveCaret(ME_TextEditor *ed); +int ME_FindPixelPos(ME_TextEditor *editor, int x, int y, ME_Cursor *result, BOOL *is_eol); +void ME_LButtonDown(ME_TextEditor *editor, int x, int y); +void ME_MouseMove(ME_TextEditor *editor, int x, int y); +void ME_DeleteTextAtCursor(ME_TextEditor *editor, int nCursor, int nChars); +void ME_InsertTextFromCursor(ME_TextEditor *editor, int nCursor, + const WCHAR *str, int len, ME_Style *style); +void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt); +BOOL ME_ArrowKey(ME_TextEditor *ed, int nVKey, int nCtrl); + +void ME_InitContext(ME_Context *c, ME_TextEditor *editor, HDC hDC); +void ME_DestroyContext(ME_Context *c); +ME_Style *GetInsertStyle(ME_TextEditor *editor, int nCursor); +void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para); +int ME_GetCursorOfs(ME_TextEditor *editor, int nCursor); +void ME_GetSelection(ME_TextEditor *editor, int *from, int *to); +int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to); +BOOL ME_IsSelection(ME_TextEditor *editor); +void ME_DeleteSelection(ME_TextEditor *editor); +void ME_SendSelChange(ME_TextEditor *editor); +void ME_InsertGraphicsFromCursor(ME_TextEditor *editor, int nCursor); +void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars); +int ME_GetTextLength(ME_TextEditor *editor); + +/* wrap.c */ +void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp); +ME_DisplayItem *ME_MakeRow(int height, int baseline, int width); +void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd); +void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp); + +/* para.c */ +ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run); +void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *editor); +ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *rp, ME_Style *style); +ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp); +void ME_DumpParaStyle(ME_Paragraph *s); +void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048]); +void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt); +void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt); +void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt); +void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt); +/* marks from first up to (but not including) last */ +void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last); +void ME_MarkAllForWrapping(ME_TextEditor *editor); + +/* paint.c */ +void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew); +void ME_Repaint(ME_TextEditor *editor); +void ME_UpdateRepaint(ME_TextEditor *editor); +void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph); +void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos); +int ME_GetScrollPos(ME_TextEditor *editor); +void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun); +COLORREF ME_GetBackColor(ME_TextEditor *editor); + +/* wintest.c */ + +/* editor.c */ +void ME_RegisterEditorClass(); +ME_TextEditor *ME_MakeEditor(HWND hWnd); +void ME_DestroyEditor(ME_TextEditor *editor); +void ME_SendOldNotify(ME_TextEditor *editor, int nCode); +ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *di); +void ME_CommitUndo(ME_TextEditor *editor); +void ME_Undo(ME_TextEditor *editor); +void ME_Redo(ME_TextEditor *editor); +void ME_EmptyUndoStack(ME_TextEditor *editor); +int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, BOOL bCRLF); + +extern int me_debug; +extern HANDLE me_heap; +extern void DoWrap(ME_TextEditor *editor); diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h new file mode 100644 index 00000000000..3520f3ad33d --- /dev/null +++ b/dlls/riched20/editstr.h @@ -0,0 +1,245 @@ +/* + * RichEdit - structures and constant + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __EDITSTR_H +#define __EDITSTR_H + +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wine/debug.h" + +typedef struct tagME_String +{ + WCHAR *szData; + int nLen, nBuffer; +} ME_String; + +typedef struct tagME_Style +{ + CHARFORMAT2W fmt; + + HFONT hFont; /* cached font for the style */ + TEXTMETRICW tm; /* cached font metrics for the style */ + int nRefs; /* reference count */ + int nSequence; /* incremented when cache needs to be rebuilt, ie. every screen redraw */ +} ME_Style; + +typedef enum { + diTextStart, /* start of the text buffer */ + diParagraph, /* paragraph start */ + diRun, /* run (sequence of chars with the same character format) */ + diStartRow, /* start of the row (line of text on the screen) */ + diTextEnd, /* end of the text buffer */ + + /********************* these below are meant for finding only *********************/ + diStartRowOrParagraph, /* 5 */ + diStartRowOrParagraphOrEnd, + diRunOrParagraph, + diRunOrStartRow, + diParagraphOrEnd, + diRunOrParagraphOrEnd, /* 10 */ + + diUndoInsertRun, /* 11 */ + diUndoDeleteRun, /* 12 */ + diUndoJoinParagraphs, /* 13 */ + diUndoSplitParagraph, /* 14 */ + diUndoSetParagraphFormat, /* 15 */ + diUndoSetCharFormat, /* 16 */ + diUndoEndTransaction, /* 17 */ + diUndoSetDefaultCharFormat, /* 18 */ +} ME_DIType; + +/******************************** run flags *************************/ +#define MERF_STYLEFLAGS 0x0FFF +/* run contains non-text content, which has its own rules for wrapping, sizing etc */ +#define MERF_GRAPHICS 1 + +/* run is splittable (contains white spaces in the middle or end) */ +#define MERF_SPLITTABLE 0x001000 +/* run starts with whitespaces */ +#define MERF_STARTWHITE 0x002000 +/* run ends with whitespaces */ +#define MERF_ENDWHITE 0x004000 +/* run is completely made of whitespaces */ +#define MERF_WHITESPACE 0x008000 +/* run is a last (dummy) run in the paragraph */ +#define MERF_SKIPPED 0x010000 +/* flags that are calculated during text wrapping */ +#define MERF_CALCBYWRAP 0x0F0000 +/* the "end of paragraph" run, contains 1 character */ +#define MERF_ENDPARA 0x100000 + +/* those flags are kept when the row is split */ +#define MERF_SPLITMASK (~(0)) + +/******************************** para flags *************************/ + +/* this paragraph was already wrapped and hasn't changed, every change resets that flag */ +#define MEPF_WRAPPED 1 +#define MEPF_REDRAW 2 + +/******************************** structures *************************/ + +struct tagME_DisplayItem; + +typedef struct tagME_Run +{ + ME_String *strText; + ME_Style *style; + int nCharOfs; /* relative to para's offset */ + int nWidth; /* width of full run, width of leading&trailing ws */ + int nFlags; + int nAscent, nDescent; /* pixels above/below baseline */ + POINT pt; /* relative to para's position */ +} ME_Run; + +typedef struct tagME_Document { + struct tagME_DisplayItem *def_char_style; + struct tagME_DisplayItem *def_para_style; + int last_wrapped_line; +} ME_Document; + +typedef struct tagME_Paragraph +{ + PARAFORMAT2 *pFmt; + int nLeftMargin, nRightMargin, nFirstMargin; + int nCharOfs; + int nFlags; + int nYPos, nHeight; + struct tagME_DisplayItem *prev_para, *next_para, *document; +} ME_Paragraph; + +typedef struct tagME_Row +{ + int nHeight; + int nBaseline; + int nWidth; + int nLMargin; + int nRMargin; + int nYPos; +} ME_Row; + +typedef struct tagME_DisplayItem +{ + ME_DIType type; + struct tagME_DisplayItem *prev, *next; + union { + ME_Run run; + ME_Row row; + ME_Paragraph para; + ME_Document doc; /* not used */ + ME_Style *ustyle; /* used by diUndoSetCharFormat */ + } member; +} ME_DisplayItem; + +typedef struct tagME_UndoItem +{ + ME_DisplayItem di; + int nStart, nLen; +} ME_UndoItem; + +typedef struct tagME_TextBuffer +{ + ME_DisplayItem *pFirst, *pLast; + ME_Style *pCharStyle; + ME_Style *pDefaultStyle; +} ME_TextBuffer; + +typedef struct tagME_Cursor +{ + ME_DisplayItem *pRun; + int nOffset; +} ME_Cursor; + +typedef enum { + umAddToUndo, + umAddToRedo, + umIgnore, + umAddBackToUndo +} ME_UndoMode; + +typedef struct tagME_TextEditor +{ + HWND hWnd; + BOOL bCaretShown; + ME_TextBuffer *pBuffer; + ME_Cursor *pCursors; + int nCursors; + SIZE sizeWindow; + int nScrollPos; + int nTotalLength; + int nUDArrowX; + int nSequence; + int nOldSelFrom, nOldSelTo; + COLORREF rgbBackColor; + BOOL bCaretAtEnd; + int nEventMask; + int nModifyStep; + ME_DisplayItem *pUndoStack, *pRedoStack; + ME_UndoMode nUndoMode; + int nParagraphs; +} ME_TextEditor; + +typedef struct tagME_Context +{ + HDC hDC; + POINT pt; + POINT ptRowOffset; + RECT rcView; + HBRUSH hbrMargin; + + /* those are valid inside ME_WrapTextParagraph and related */ + POINT ptFirstRun; + ME_TextEditor *editor; + int nSequence; +} ME_Context; + +typedef struct tagME_WrapContext +{ + ME_Style *style; + ME_Context *context; + int nLeftMargin, nRightMargin, nFirstMargin; + int nTotalWidth, nAvailWidth; + int nRow; + POINT pt; + BOOL bOverflown; + ME_DisplayItem *pRowStart; + + ME_DisplayItem *pLastSplittableRun; + POINT ptLastSplittableRun; +} ME_WrapContext; + +#endif diff --git a/dlls/riched20/list.c b/dlls/riched20/list.c new file mode 100644 index 00000000000..0480e28adc1 --- /dev/null +++ b/dlls/riched20/list.c @@ -0,0 +1,191 @@ +/* + * RichEdit - Basic operations on double linked lists. + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(riched20); + +void ME_InsertBefore(ME_DisplayItem *diWhere, ME_DisplayItem *diWhat) +{ + diWhat->next = diWhere; + diWhat->prev = diWhere->prev; + + diWhere->prev->next = diWhat; + diWhat->next->prev = diWhat; +} + +void ME_Remove(ME_DisplayItem *diWhere) +{ + ME_DisplayItem *diNext = diWhere->next; + ME_DisplayItem *diPrev = diWhere->prev; + assert(diNext); + assert(diPrev); + diPrev->next = diNext; + diNext->prev = diPrev; +} + +ME_DisplayItem *ME_FindItemBack(ME_DisplayItem *di, ME_DIType nTypeOrClass) +{ + if (!di) + return NULL; + di = di->prev; + while(di!=NULL) { + if (ME_DITypesEqual(di->type, nTypeOrClass)) + return di; + di = di->prev; + } + return NULL; +} + +ME_DisplayItem *ME_FindItemBackOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass) +{ + while(di!=NULL) { + if (ME_DITypesEqual(di->type, nTypeOrClass)) + return di; + di = di->prev; + } + return NULL; +} + +ME_DisplayItem *ME_FindItemFwd(ME_DisplayItem *di, ME_DIType nTypeOrClass) +{ + if (!di) return NULL; + di = di->next; + while(di!=NULL) { + if (ME_DITypesEqual(di->type, nTypeOrClass)) + return di; + di = di->next; + } + return NULL; +} + +ME_DisplayItem *ME_FindItemFwdOrHere(ME_DisplayItem *di, ME_DIType nTypeOrClass) +{ + while(di!=NULL) { + if (ME_DITypesEqual(di->type, nTypeOrClass)) + return di; + di = di->next; + } + return NULL; +} + +BOOL ME_DITypesEqual(ME_DIType type, ME_DIType nTypeOrClass) +{ + if (type==nTypeOrClass) + return TRUE; + if (nTypeOrClass==diRunOrParagraph && (type==diRun || type==diParagraph)) + return TRUE; + if (nTypeOrClass==diRunOrStartRow && (type==diRun || type==diStartRow)) + return TRUE; + if (nTypeOrClass==diParagraphOrEnd && (type==diTextEnd || type==diParagraph)) + return TRUE; + if (nTypeOrClass==diStartRowOrParagraph && (type==diStartRow || type==diParagraph)) + return TRUE; + if (nTypeOrClass==diStartRowOrParagraphOrEnd + && (type==diStartRow || type==diParagraph || type==diTextEnd)) + return TRUE; + if (nTypeOrClass==diRunOrParagraphOrEnd + && (type==diRun || type==diParagraph || type==diTextEnd)) + return TRUE; + return FALSE; +} + +void ME_DestroyDisplayItem(ME_DisplayItem *item) { +/* TRACE("type=%s\n", ME_GetDITypeName(item->type)); */ + if (item->type==diParagraph || item->type == diUndoSetParagraphFormat) { + FREE_OBJ(item->member.para.pFmt); + } + if (item->type==diRun || item->type == diUndoInsertRun) { + ME_ReleaseStyle(item->member.run.style); + ME_DestroyString(item->member.run.strText); + } + if (item->type==diUndoSetCharFormat || item->type==diUndoSetDefaultCharFormat) { + ME_ReleaseStyle(item->member.ustyle); + } + FREE_OBJ(item); +} + +ME_DisplayItem *ME_MakeDI(ME_DIType type) { + ME_DisplayItem *item = ALLOC_OBJ(ME_DisplayItem); + ZeroMemory(item, sizeof(ME_DisplayItem)); + item->type = type; + item->prev = item->next = NULL; + if (type == diParagraph || type == diUndoSplitParagraph) { + item->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); + item->member.para.pFmt->cbSize = sizeof(PARAFORMAT2); + item->member.para.pFmt->dwMask = 0; + } + + return item; +} + +const char *ME_GetDITypeName(ME_DIType type) +{ + switch(type) + { + case diParagraph: return "diParagraph"; + case diRun: return "diRun"; + case diTextStart: return "diTextStart"; + case diTextEnd: return "diTextEnd"; + case diStartRow: return "diStartRow"; + case diUndoEndTransaction: return "diUndoEndTransaction"; + case diUndoSetParagraphFormat: return "diUndoSetParagraphFormat"; + case diUndoSetCharFormat: return "diUndoSetCharFormat"; + case diUndoInsertRun: return "diUndoInsertRun"; + case diUndoDeleteRun: return "diUndoDeleteRun"; + case diUndoJoinParagraphs: return "diJoinParagraphs"; + case diUndoSplitParagraph: return "diSplitParagraph"; + case diUndoSetDefaultCharFormat: return "diUndoSetDefaultCharFormat"; + default: return "?"; + } +} + +void ME_DumpDocument(ME_TextBuffer *buffer) +{ + /* FIXME this is useless, */ + ME_DisplayItem *pItem = buffer->pFirst; + TRACE("DOCUMENT DUMP START\n"); + while(pItem) { + switch(pItem->type) + { + case diTextStart: + TRACE("Start"); + break; + case diParagraph: + TRACE("\nParagraph(ofs=%d)", pItem->member.para.nCharOfs); + break; + case diStartRow: + TRACE(" - StartRow"); + break; + case diRun: + TRACE(" - Run(\"%s\", %d)", debugstr_w(pItem->member.run.strText->szData), + pItem->member.run.nCharOfs); + break; + case diTextEnd: + TRACE("\nEnd\n"); + break; + default: + break; + } + pItem = pItem->next; + } + TRACE("DOCUMENT DUMP END\n"); +} diff --git a/dlls/riched20/paint.c b/dlls/riched20/paint.c new file mode 100644 index 00000000000..2c1d6397dcb --- /dev/null +++ b/dlls/riched20/paint.c @@ -0,0 +1,359 @@ +/* + * RichEdit - painting functions + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +void ME_PaintContent(ME_TextEditor *editor, HDC hDC, BOOL bOnlyNew) { + ME_DisplayItem *item; + ME_Context c; + + editor->nSequence++; + ME_InitContext(&c, editor, hDC); + SetBkMode(hDC, TRANSPARENT); + ME_MoveCaret(editor); + item = editor->pBuffer->pFirst->next; + c.pt.y=-GetScrollPos(editor->hWnd, SB_VERT); + while(item != editor->pBuffer->pLast) { + assert(item->type == diParagraph); + if (!bOnlyNew || (item->member.para.nFlags & MEPF_REDRAW)) + { + ME_DrawParagraph(&c, item); + item->member.para.nFlags &= ~MEPF_REDRAW; + } + c.pt.y += item->member.para.nHeight; + item = item->member.para.next_para; + } + /* FIXME this code just sucks, it should try to redraw incrementally */ + if (c.pt.ypBuffer->pFirst->next; + while(item != editor->pBuffer->pLast) { + if (item->type == diRun) + ME_UnprepareStyle(item->member.run.style); + item = item->next; + } + +} + +void ME_Repaint(ME_TextEditor *editor) +{ + ME_Cursor *pCursor = &editor->pCursors[0]; + ME_DisplayItem *pRun = NULL; + int nOffset = -1; + HDC hDC; + + int nCharOfs = ME_CharOfsFromRunOfs(editor, pCursor->pRun, pCursor->nOffset); + ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset); + assert(pRun == pCursor->pRun); + assert(nOffset == pCursor->nOffset); + DoWrap(editor); + hDC = GetDC(editor->hWnd); + ME_HideCaret(editor); + ME_PaintContent(editor, hDC, TRUE); + ReleaseDC(editor->hWnd, hDC); + ME_ShowCaret(editor); +} + +void ME_UpdateRepaint(ME_TextEditor *editor) +{ + InvalidateRect(editor->hWnd, NULL, TRUE); + ME_SendOldNotify(editor, EN_CHANGE); + ME_Repaint(editor); + ME_SendOldNotify(editor, EN_UPDATE); + ME_SendSelChange(editor); +} + +void ME_DrawTextWithStyle(ME_Context *c, int x, int y, LPCWSTR szText, int nChars, + ME_Style *s, int *width, int nSelFrom, int nSelTo, int ymin, int cy) { + HDC hDC = c->hDC; + HGDIOBJ hOldFont; + COLORREF rgbOld, rgbBack; + ME_PrepareStyle(c, s); + hOldFont = SelectObject(hDC, s->hFont); + rgbBack = ME_GetBackColor(c->editor); + if ((s->fmt.dwMask & CFM_COLOR) && (s->fmt.dwEffects & CFE_AUTOCOLOR)) + rgbOld = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT)); + else + rgbOld = SetTextColor(hDC, s->fmt.crTextColor); + ExtTextOutW(hDC, x, y, 0, NULL, szText, nChars, NULL); + if (width) { + SIZE sz; + GetTextExtentPoint32W(hDC, szText, nChars, &sz); + *width = sz.cx; + } + if (nSelFrom < nChars && nSelTo >= 0 && nSelFrom nChars) nSelTo = nChars; + GetTextExtentPoint32W(hDC, szText, nSelFrom, &sz); + x += sz.cx; + GetTextExtentPoint32W(hDC, szText+nSelFrom, nSelTo-nSelFrom, &sz); + PatBlt(hDC, x, ymin, sz.cx, cy, DSTINVERT); + } + SetTextColor(hDC, rgbOld); + SelectObject(hDC, hOldFont); +} + +void ME_DrawSelection(ME_Context *c) +{ +} + +void ME_DebugWrite(HDC hDC, POINT *pt, WCHAR *szText) { + int align = SetTextAlign(hDC, TA_LEFT|TA_TOP); + HGDIOBJ hFont = SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT)); + COLORREF color = SetTextColor(hDC, RGB(128,128,128)); + TextOutW(hDC, pt->x, pt->y, szText, lstrlenW(szText)); + SelectObject(hDC, hFont); + SetTextAlign(hDC, align); + SetTextColor(hDC, color); +} + +void ME_DrawGraphics(ME_Context *c, int x, int y, ME_Run *run, + ME_Paragraph *para, BOOL selected) { + SIZE sz; + int xs, ys, xe, ye, h, ym, width, eyes; + ME_GetGraphicsSize(c->editor, run, &sz); + xs = run->pt.x; + ys = y-sz.cy; + xe = xs+sz.cx; + ye = y; + h = ye-ys; + ym = ys+h/4; + width = sz.cx; + eyes = width/8; + /* draw a smiling face :) */ + Ellipse(c->hDC, xs, ys, xe, ye); + Ellipse(c->hDC, xs+width/8, ym, x+width/8+eyes, ym+eyes); + Ellipse(c->hDC, xs+7*width/8-eyes, ym, xs+7*width/8, ym+eyes); + MoveToEx(c->hDC, xs+width/8, ys+3*h/4-eyes, NULL); + LineTo(c->hDC, xs+width/8, ys+3*h/4); + LineTo(c->hDC, xs+7*width/8, ys+3*h/4); + LineTo(c->hDC, xs+7*width/8, ys+3*h/4-eyes); + if (selected) + { + /* descent is usually (always?) 0 for graphics */ + PatBlt(c->hDC, x, y-run->nAscent, sz.cx, run->nAscent+run->nDescent, DSTINVERT); + } +} + +void ME_DrawRun(ME_Context *c, int x, int y, ME_DisplayItem *rundi, ME_Paragraph *para) { + ME_Run *run = &rundi->member.run; + int runofs = run->nCharOfs+para->nCharOfs; + + if (run->nFlags & MERF_GRAPHICS) { + int blfrom, blto; + ME_GetSelection(c->editor, &blfrom, &blto); + ME_DrawGraphics(c, x, y, run, para, (runofs >= blfrom) && (runofs < blto)); + } else + { + int blfrom, blto; + ME_DisplayItem *start = ME_FindItemBack(rundi, diStartRow); + ME_GetSelection(c->editor, &blfrom, &blto); + + ME_DrawTextWithStyle(c, x, y, + run->strText->szData, ME_StrVLen(run->strText), run->style, NULL, + blfrom-runofs, blto-runofs, c->pt.y+start->member.row.nYPos, start->member.row.nHeight); + } +} + +COLORREF ME_GetBackColor(ME_TextEditor *editor) +{ +/* Looks like I was seriously confused + return GetSysColor((GetWindowLong(editor->hWnd, GWL_STYLE) & ES_READONLY) ? COLOR_3DFACE: COLOR_WINDOW); +*/ + if (editor->rgbBackColor == -1) + return GetSysColor(COLOR_WINDOW); + else + return editor->rgbBackColor; +} + +void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph) { + int align = SetTextAlign(c->hDC, TA_BASELINE); + ME_DisplayItem *p; + ME_Run *run; + ME_Paragraph *para = NULL; + RECT rc, rcPara; + int y = c->pt.y; + int height = 0, baseline = 0, no=0, pno = 0; + int xs, xe; + int visible = 0; + int nMargWidth = 0; + + c->pt.x = c->rcView.left; + rcPara.left = c->rcView.left; + rcPara.right = c->rcView.right; + for (p = paragraph; p!=paragraph->member.para.next_para; p = p->next) { + switch(p->type) { + case diParagraph: + para = &p->member.para; + break; + case diStartRow: + assert(para); + nMargWidth = (pno==0?para->nFirstMargin:para->nLeftMargin); + xs = c->rcView.left+nMargWidth; + xe = c->rcView.right-para->nRightMargin; + y += height; + rcPara.top = y; + rcPara.bottom = y+p->member.row.nHeight; + visible = RectVisible(c->hDC, &rcPara); + if (visible) { + HBRUSH hbr; + /* left margin */ + rc.left = c->rcView.left; + rc.right = c->rcView.left+nMargWidth; + rc.top = y; + rc.bottom = y+p->member.row.nHeight; + FillRect(c->hDC, &rc, c->hbrMargin); + /* right margin */ + rc.left = xe; + rc.right = c->rcView.right; + FillRect(c->hDC, &rc, c->hbrMargin); + rc.left = c->rcView.left+para->nLeftMargin; + rc.right = xe; + hbr = CreateSolidBrush(ME_GetBackColor(c->editor)); + FillRect(c->hDC, &rc, hbr); + DeleteObject(hbr); + } + if (me_debug) + { + const WCHAR wszRowDebug[] = {'r','o','w','[','%','d',']',0}; + WCHAR buf[128]; + POINT pt = c->pt; + wsprintfW(buf, wszRowDebug, no); + pt.y = 12+y; + ME_DebugWrite(c->hDC, &pt, buf); + } + + height = p->member.row.nHeight; + baseline = p->member.row.nBaseline; + pno++; + break; + case diRun: + assert(para); + run = &p->member.run; + if (visible && me_debug) { + rc.left = c->rcView.left+run->pt.x; + rc.right = c->rcView.left+run->pt.x+run->nWidth; + rc.top = c->pt.y+run->pt.y; + rc.bottom = c->pt.y+run->pt.y+height; + TRACE("rc = (%ld, %ld, %ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom); + if (run->nFlags & MERF_SKIPPED) + DrawFocusRect(c->hDC, &rc); + else + FrameRect(c->hDC, &rc, GetSysColorBrush(COLOR_GRAYTEXT)); + } + if (visible) + ME_DrawRun(c, run->pt.x, c->pt.y+run->pt.y+baseline, p, ¶graph->member.para); + if (me_debug) + { + /* I'm using %ls, hope wsprintfW is not going to use wrong (4-byte) WCHAR version */ + const WCHAR wszRunDebug[] = {'[','%','d',':','%','x',']',' ','%','l','s',0}; + WCHAR buf[2560]; + POINT pt; + pt.x = run->pt.x; + pt.y = c->pt.y + run->pt.y; + wsprintfW(buf, wszRunDebug, no, p->member.run.nFlags, p->member.run.strText->szData); + ME_DebugWrite(c->hDC, &pt, buf); + } + /* c->pt.x += p->member.run.nWidth; */ + break; + default: + break; + } + no++; + } + SetTextAlign(c->hDC, align); +} + +void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos) +{ + float perc = 0.0; + SCROLLINFO si; + HWND hWnd = editor->hWnd; + int overflow = editor->nTotalLength - editor->sizeWindow.cy; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS; + GetScrollInfo(hWnd, SB_VERT, &si); + + if (ypos < 0) { + perc = 1.0*si.nPos/si.nMax; + ypos = perc*overflow; + } + if (ypos >= overflow && overflow > 0) + ypos = overflow - 1; + + if (overflow > 0) { + EnableScrollBar(hWnd, SB_VERT, ESB_ENABLE_BOTH); + SetScrollRange(hWnd, SB_VERT, 0, overflow, FALSE); + SetScrollPos(hWnd, SB_VERT, ypos, TRUE); + } else { + EnableScrollBar(hWnd, SB_VERT, ESB_DISABLE_BOTH); + SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE); + SetScrollPos(hWnd, SB_VERT, 0, TRUE); + } + if (ypos != si.nPos) + { + TRACE("ScrollWindow(%d, %d, %d, %0.4f)\n", si.nPos, si.nMax, ypos, perc); + ScrollWindow(hWnd, 0, si.nPos - ypos, NULL, NULL); + UpdateWindow(hWnd); + } +} + +int ME_GetScrollPos(ME_TextEditor *editor) +{ + return GetScrollPos(editor->hWnd, SB_VERT); +} + +void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun) +{ + ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow); + ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph); + int y, yrel, yheight; + + assert(pRow); + assert(pPara); + + y = pPara->member.para.nYPos+pRow->member.row.nYPos; + yheight = pRow->member.row.nHeight; + yrel = y - ME_GetScrollPos(editor); + if (yrel < 0) + ME_UpdateScrollBar(editor, y); + else if (yrel + yheight > editor->sizeWindow.cy) + { + ME_UpdateScrollBar(editor, y + yheight - editor->sizeWindow.cy); + } +} diff --git a/dlls/riched20/para.c b/dlls/riched20/para.c new file mode 100644 index 00000000000..b43713922b1 --- /dev/null +++ b/dlls/riched20/para.c @@ -0,0 +1,358 @@ +/* + * RichEdit - functions working on paragraphs of text (diParagraph). + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +static WCHAR wszParagraphSign[] = {0xB6, 0}; + +void ME_MakeFirstParagraph(HDC hDC, ME_TextBuffer *text) +{ + PARAFORMAT2 fmt; + CHARFORMAT2W cf; + LOGFONTW lf; + HFONT hf; + ME_DisplayItem *para = ME_MakeDI(diParagraph); + ME_DisplayItem *run; + ME_Style *style; + + hf = (HFONT)GetStockObject(SYSTEM_FONT); + assert(hf); + GetObjectW(hf, sizeof(LOGFONTW), &lf); + ZeroMemory(&cf, sizeof(cf)); + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_BACKCOLOR|CFM_COLOR|CFM_FACE|CFM_SIZE|CFM_CHARSET; + cf.dwMask |= CFM_ALLCAPS|CFM_BOLD|CFM_DISABLED|CFM_EMBOSS|CFM_HIDDEN; + cf.dwMask |= CFM_IMPRINT|CFM_ITALIC|CFM_LINK|CFM_OUTLINE|CFM_PROTECTED; + cf.dwMask |= CFM_REVISED|CFM_SHADOW|CFM_SMALLCAPS|CFM_STRIKEOUT; + cf.dwMask |= CFM_SUBSCRIPT|CFM_UNDERLINE; + + cf.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR; + lstrcpyW(cf.szFaceName, lf.lfFaceName); + cf.yHeight=lf.lfHeight*1440/GetDeviceCaps(hDC, LOGPIXELSY); + if (lf.lfWeight>=700) /* FIXME correct weight ? */ + cf.dwEffects |= CFE_BOLD; + cf.wWeight = lf.lfWeight; + if (lf.lfItalic) cf.dwEffects |= CFE_ITALIC; + if (lf.lfUnderline) cf.dwEffects |= CFE_UNDERLINE; + if (lf.lfStrikeOut) cf.dwEffects |= CFE_STRIKEOUT; + + ZeroMemory(&fmt, sizeof(fmt)); + fmt.cbSize = sizeof(fmt); + fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT; + + CopyMemory(para->member.para.pFmt, &fmt, sizeof(PARAFORMAT2)); + + style = ME_MakeStyle(&cf); + text->pDefaultStyle = style; + + run = ME_MakeRun(style, ME_MakeString(wszParagraphSign), MERF_ENDPARA); + run->member.run.nCharOfs = 0; + + ME_InsertBefore(text->pLast, para); + ME_InsertBefore(text->pLast, run); + para->member.para.prev_para = text->pFirst; + para->member.para.next_para = text->pLast; + text->pFirst->member.para.next_para = para; + text->pLast->member.para.prev_para = para; + + text->pLast->member.para.nCharOfs = 1; +} + +void ME_MarkAllForWrapping(ME_TextEditor *editor) +{ + ME_MarkForWrapping(editor, editor->pBuffer->pFirst->member.para.next_para, editor->pBuffer->pLast); +} + +void ME_MarkForWrapping(ME_TextEditor *editor, ME_DisplayItem *first, ME_DisplayItem *last) +{ + while(first != last) + { + first->member.para.nFlags &= ~MEPF_WRAPPED; + first = first->member.para.next_para; + } +} + +/* split paragraph at the beginning of the run */ +ME_DisplayItem *ME_SplitParagraph(ME_TextEditor *editor, ME_DisplayItem *run, ME_Style *style) +{ + ME_DisplayItem *next_para = NULL; + ME_DisplayItem *run_para = NULL; + ME_DisplayItem *new_para = ME_MakeDI(diParagraph); + ME_DisplayItem *end_run = ME_MakeRun(style,ME_MakeString(wszParagraphSign), MERF_ENDPARA); + ME_UndoItem *undo = NULL; + int ofs; + ME_DisplayItem *pp; + + assert(run->type == diRun); + + run_para = ME_GetParagraph(run); + assert(run_para->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); + + ofs = end_run->member.run.nCharOfs = run->member.run.nCharOfs; + next_para = run_para->member.para.next_para; + assert(next_para == ME_FindItemFwd(run_para, diParagraphOrEnd)); + + undo = ME_AddUndoItem(editor, diUndoJoinParagraphs, NULL); + if (undo) + undo->nStart = run_para->member.para.nCharOfs + ofs; + + /* the new paragraph will have a different starting offset, so let's update its runs */ + pp = run; + while(pp->type == diRun) { + pp->member.run.nCharOfs -= ofs; + pp = ME_FindItemFwd(pp, diRunOrParagraphOrEnd); + } + new_para->member.para.nCharOfs = ME_GetParagraph(run)->member.para.nCharOfs+ofs; + new_para->member.para.nCharOfs += 1; + + new_para->member.para.nFlags = 0; /* FIXME copy flags (if applicable) */ + /* FIXME initialize format style and call ME_SetParaFormat blah blah */ + CopyMemory(new_para->member.para.pFmt, run_para->member.para.pFmt, sizeof(PARAFORMAT2)); + + /* FIXME remove this as soon as nLeftMargin etc are replaced with proper fields of PARAFORMAT2 */ + new_para->member.para.nLeftMargin = run_para->member.para.nLeftMargin; + new_para->member.para.nRightMargin = run_para->member.para.nRightMargin; + new_para->member.para.nFirstMargin = run_para->member.para.nFirstMargin; + + /* insert paragraph into paragraph double linked list */ + new_para->member.para.prev_para = run_para; + new_para->member.para.next_para = next_para; + run_para->member.para.next_para = new_para; + next_para->member.para.prev_para = new_para; + + /* insert end run of the old paragraph, and new paragraph, into DI double linked list */ + ME_InsertBefore(run, new_para); + ME_InsertBefore(new_para, end_run); + + /* force rewrap of the */ + run_para->member.para.prev_para->member.para.nFlags &= ~MEPF_WRAPPED; + new_para->member.para.prev_para->member.para.nFlags &= ~MEPF_WRAPPED; + + /* we've added the end run, so we need to modify nCharOfs in the next paragraphs */ + ME_PropagateCharOffset(next_para, 1); + editor->nParagraphs++; + + return new_para; +} + +/* join tp with tp->member.para.next_para, keeping tp's style; this + * is consistent with the original */ +ME_DisplayItem *ME_JoinParagraphs(ME_TextEditor *editor, ME_DisplayItem *tp) +{ + ME_DisplayItem *pNext, *pFirstRunInNext, *pRun, *pTmp; + int i, shift; + ME_UndoItem *undo = NULL; + + assert(tp->type == diParagraph); + assert(tp->member.para.next_para); + assert(tp->member.para.next_para->type == diParagraph); + + pNext = tp->member.para.next_para; + + { + /* null char format operation to store the original char format for the ENDPARA run */ + CHARFORMAT2W fmt; + ME_InitCharFormat2W(&fmt); + ME_SetCharFormat(editor, pNext->member.para.nCharOfs-1, 1, &fmt); + } + undo = ME_AddUndoItem(editor, diUndoSplitParagraph, NULL); + if (undo) + { + undo->nStart = pNext->member.para.nCharOfs-1; + assert(pNext->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); + CopyMemory(undo->di.member.para.pFmt, pNext->member.para.pFmt, sizeof(PARAFORMAT2)); + } + + shift = pNext->member.para.nCharOfs - tp->member.para.nCharOfs - 1; + + pRun = ME_FindItemBack(pNext, diRunOrParagraph); + pFirstRunInNext = ME_FindItemFwd(pNext, diRunOrParagraph); + + assert(pRun); + assert(pRun->type == diRun); + assert(pRun->member.run.nFlags & MERF_ENDPARA); + assert(pFirstRunInNext->type == diRun); + + /* if some cursor points at end of paragraph, make it point to the first + run of the next joined paragraph */ + for (i=0; inCursors; i++) { + if (editor->pCursors[i].pRun == pRun) { + editor->pCursors[i].pRun = pFirstRunInNext; + editor->pCursors[i].nOffset = 0; + } + } + + pTmp = pNext; + do { + pTmp = ME_FindItemFwd(pTmp, diRunOrParagraphOrEnd); + if (pTmp->type != diRun) + break; + TRACE("shifting \"%s\" by %d (previous %d)\n", debugstr_w(pTmp->member.run.strText->szData), shift, pTmp->member.run.nCharOfs); + pTmp->member.run.nCharOfs += shift; + } while(1); + + ME_Remove(pRun); + ME_DestroyDisplayItem(pRun); + + tp->member.para.next_para = pNext->member.para.next_para; + pNext->member.para.next_para->member.para.prev_para = tp; + ME_Remove(pNext); + ME_DestroyDisplayItem(pNext); + + ME_PropagateCharOffset(tp->member.para.next_para, -1); + + ME_CheckCharOffsets(editor); + + editor->nParagraphs--; + tp->member.para.nFlags &= ~MEPF_WRAPPED; + return tp; +} + +ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *item) { + return ME_FindItemBackOrHere(item, diParagraph); +} + +static void ME_DumpStyleEffect(char **p, const char *name, PARAFORMAT2 *fmt, int mask) +{ + *p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->wEffects & mask) ? "yes" : "no") : "N/A"); +} + +void ME_DumpParaStyleToBuf(PARAFORMAT2 *pFmt, char buf[2048]) +{ + /* FIXME only PARAFORMAT styles implemented */ + char *p; + p = buf; + p += sprintf(p, "Alignment: %s\n", + !(pFmt->dwMask & PFM_ALIGNMENT) ? "N/A" : + ((pFmt->wAlignment == PFA_LEFT) ? "left" : + ((pFmt->wAlignment == PFA_RIGHT) ? "right" : + ((pFmt->wAlignment == PFA_CENTER) ? "center" : + /*((pFmt->wAlignment == PFA_JUSTIFY) ? "justify" : "incorrect")*/ + "incorrect")))); + + if (pFmt->dwMask & PFM_OFFSET) + p += sprintf(p, "Offset: %d\n", (int)pFmt->dxOffset); + else + p += sprintf(p, "Offset: N/A\n"); + + if (pFmt->dwMask & PFM_OFFSETINDENT) + p += sprintf(p, "Offset indent: %d\n", (int)pFmt->dxStartIndent); + else + p += sprintf(p, "Offset indent: N/A\n"); + + if (pFmt->dwMask & PFM_STARTINDENT) + p += sprintf(p, "Start indent: %d\n", (int)pFmt->dxStartIndent); + else + p += sprintf(p, "Start indent: N/A\n"); + + if (pFmt->dwMask & PFM_RIGHTINDENT) + p += sprintf(p, "Right indent: %d\n", (int)pFmt->dxRightIndent); + else + p += sprintf(p, "Right indent: N/A\n"); + + ME_DumpStyleEffect(&p, "Page break before:", pFmt, PFM_PAGEBREAKBEFORE); +} + +void ME_SetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt) +{ + PARAFORMAT2 copy; + assert(sizeof(*para->member.para.pFmt) == sizeof(PARAFORMAT2)); + ME_AddUndoItem(editor, diUndoSetParagraphFormat, para); + + CopyMemory(©, para->member.para.pFmt, sizeof(PARAFORMAT2)); + + if (pFmt->dwMask & PFM_ALIGNMENT) + para->member.para.pFmt->wAlignment = pFmt->wAlignment; + + /* FIXME to be continued (indents, bulleting and such) */ + + if (memcmp(©, para->member.para.pFmt, sizeof(PARAFORMAT2))) + para->member.para.nFlags &= ~MEPF_WRAPPED; +} + +void ME_SetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) +{ + int nFrom, nTo; + ME_DisplayItem *para, *para_end, *run; + int nOffset; + + ME_GetSelection(editor, &nFrom, &nTo); + if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */ + nTo--; + + ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset); + para = ME_GetParagraph(run); + ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset); + para_end = ME_GetParagraph(run); + + do { + ME_SetParaFormat(editor, para, pFmt); + if (para == para_end) + break; + para = para->member.para.next_para; + } while(1); + ME_Repaint(editor); +} + +void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt) +{ + if (pFmt->cbSize >= sizeof(PARAFORMAT2)) + { + CopyMemory(pFmt, para->member.para.pFmt, sizeof(PARAFORMAT2)); + return; + } + CopyMemory(pFmt, para->member.para.pFmt, pFmt->cbSize); +} + +void ME_GetSelectionParaFormat(ME_TextEditor *editor, PARAFORMAT2 *pFmt) +{ + int nFrom, nTo; + ME_DisplayItem *para, *para_end, *run; + int nOffset; + PARAFORMAT2 tmp; + + ME_GetSelection(editor, &nFrom, &nTo); + if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */ + nTo--; + + ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset); + para = ME_GetParagraph(run); + ME_RunOfsFromCharOfs(editor, nTo, &run, &nOffset); + para_end = ME_GetParagraph(run); + + ME_GetParaFormat(editor, para, pFmt); + if (para == para_end) return; + + do { + ZeroMemory(&tmp, sizeof(tmp)); + tmp.cbSize = sizeof(tmp); + ME_GetParaFormat(editor, para, &tmp); + assert(tmp.dwMask & PFM_ALIGNMENT); + + if (pFmt->wAlignment != tmp.wAlignment) + pFmt->dwMask &= ~PFM_ALIGNMENT; + + if (para == para_end) + return; + para = para->member.para.next_para; + } while(1); +} diff --git a/dlls/riched20/riched20.spec b/dlls/riched20/riched20.spec new file mode 100644 index 00000000000..aa71eec8f77 --- /dev/null +++ b/dlls/riched20/riched20.spec @@ -0,0 +1,9 @@ +2 extern IID_IRichEditOle +3 extern IID_IRichEditOleCallback +4 stdcall CreateTextServices(ptr ptr ptr) +5 extern IID_ITextServices +6 extern IID_ITextHost +7 extern IID_ITextHost2 +8 stdcall REExtendedRegisterClass() +9 stdcall RichEdit10ANSIWndProc(ptr long long long) +10 stdcall RichEditANSIWndProc(ptr long long long) diff --git a/dlls/riched20/richole.c b/dlls/riched20/richole.c new file mode 100644 index 00000000000..f9457fd024c --- /dev/null +++ b/dlls/riched20/richole.c @@ -0,0 +1,36 @@ +/* + * RichEdit GUIDs + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* there is no way to be consistent across different sets of headers - mingw, Wine, Win32 SDK*/ + +#define RICHEDIT_GUID(name, l, w1, w2) \ + GUID name = { l, w1, w2, {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} + +RICHEDIT_GUID(IID_IRichEditOle, 0x00020d00, 0, 0); +RICHEDIT_GUID(IID_IRichEditOleCallback, 0x00020d03, 0, 0); + +#define TEXTSERV_GUID(name, l, w1, w2, b1, b2) \ + GUID name = { l, w1, w2, {b1, b2, 0x00, 0xaa, 0x00, 0x6c, 0xad, 0xc5}} + +TEXTSERV_GUID(IID_ITextServices, 0x8d33f740, 0xcf58, 0x11ce, 0xa8, 0x9d); +TEXTSERV_GUID(IID_ITextHost, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e); +TEXTSERV_GUID(IID_ITextHost2, 0xc5bdd8d0, 0xd26e, 0x11ce, 0xa8, 0x9e); diff --git a/dlls/riched20/row.c b/dlls/riched20/row.c new file mode 100644 index 00000000000..1e4e208a97a --- /dev/null +++ b/dlls/riched20/row.c @@ -0,0 +1,84 @@ +/* + * RichEdit - Operations on rows of text (rows are recreated during + * wrapping and are used for displaying the document, they don't keep any + * true document content; delete all rows, rewrap all paragraphs and + * you get them back). + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "editor.h" + +ME_DisplayItem *ME_FindRowStart(ME_Context *c, ME_DisplayItem *item, + int nRelPos) { + ME_DisplayItem *para = ME_GetParagraph(item); + ME_MustBeWrapped(c, para); + if(nRelPos>=0) { /* if this or preceding row */ + while(nRelPos<=0) { + ME_DisplayItem *item2 = ME_FindItemBack(item, diStartRowOrParagraph); + if (item2->type == diParagraph) + { + if (item2->member.para.prev_para == NULL) + return item; + /* if skipping to the preceding paragraph, ensure it's wrapped */ + ME_MustBeWrapped(c, item2->member.para.prev_para); + item = item2; + continue; + } + else if (item2->type == diStartRow) + { + nRelPos++; + if (nRelPos>0) + return item; + item = item2; + continue; + } + assert(0 == "bug in FindItemBack(item, diStartRowOrParagraph)"); + item = item2; + } + return item; + } + while(nRelPos>0) { /* if one of the next rows */ + ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraph); + if (!item2) + return item; + if (item2->type == diParagraph) + { + if (item2->member.para.next_para == NULL) + return item; + continue; + } + item = item2; + nRelPos--; + } + return item; +} + +/* I'm sure these functions would simplify some code in caret ops etc, + * I just didn't remember them when I wrote that code + */ + +ME_DisplayItem *ME_RowStart(ME_DisplayItem *item) { + return ME_FindItemBackOrHere(item, diStartRow); +} + +ME_DisplayItem *ME_RowEnd(ME_DisplayItem *item) { + ME_DisplayItem *item2 = ME_FindItemFwd(item, diStartRowOrParagraphOrEnd); + if (!item2) return NULL; + return ME_FindItemBack(item, diRun); +} diff --git a/dlls/riched20/run.c b/dlls/riched20/run.c new file mode 100644 index 00000000000..4a4437bb9f0 --- /dev/null +++ b/dlls/riched20/run.c @@ -0,0 +1,672 @@ +/* + * RichEdit - operations on runs (diRun, rectangular pieces of paragraphs). + * Splitting/joining runs. Adjusting offsets after deleting/adding content. + * Character/pixel conversions. + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2) +{ + if ((run1->nFlags | run2->nFlags) & (MERF_ENDPARA | MERF_GRAPHICS)) + return 0; + if (run1->style != run2->style) + return 0; + if ((run1->nFlags & MERF_STYLEFLAGS) != (run2->nFlags & MERF_STYLEFLAGS)) + return 0; + return 1; +} + +void ME_SkipAndPropagateCharOffset(ME_DisplayItem *p, int shift) +{ + p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); + assert(p); + ME_PropagateCharOffset(p, shift); +} + +void ME_PropagateCharOffset(ME_DisplayItem *p, int shift) +{ + if (p->type == diRun) /* propagate in all runs in this para */ + { + TRACE("PropagateCharOffset(%s, %d)\n", debugstr_w(p->member.run.strText->szData), shift); + do { + p->member.run.nCharOfs += shift; + assert(p->member.run.nCharOfs >= 0); + p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); + } while(p->type == diRun); + } + if (p->type == diParagraph) /* propagate in all next paras */ + { + do { + p->member.para.nCharOfs += shift; + assert(p->member.para.nCharOfs >= 0); + p = p->member.para.next_para; + } while(p->type == diParagraph); + } + if (p->type == diTextEnd) + { + p->member.para.nCharOfs += shift; + assert(p->member.para.nCharOfs >= 0); + } +} + +void ME_CheckCharOffsets(ME_TextEditor *editor) +{ + ME_DisplayItem *p = editor->pBuffer->pFirst; + int ofs = 0, ofsp = 0; + if(TRACE_ON(richedit)) + { + TRACE("---\n"); + ME_DumpDocument(editor->pBuffer); + } + do { + p = ME_FindItemFwd(p, diRunOrParagraphOrEnd); + switch(p->type) { + case diTextEnd: + TRACE("tend, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs); + assert(ofsp+ofs == p->member.para.nCharOfs); + return; + case diParagraph: + TRACE("para, real ofsp = %d, counted = %d\n", p->member.para.nCharOfs, ofsp+ofs); + assert(ofsp+ofs == p->member.para.nCharOfs); + ofsp = p->member.para.nCharOfs; + ofs = 0; + break; + case diRun: + TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n", + p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs, + p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData), + p->member.run.nFlags, + p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects); + assert(ofs == p->member.run.nCharOfs); + ofs += ME_StrLen(p->member.run.strText); + break; + default: + assert(0); + } + } while(1); +} + +int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs) +{ + ME_DisplayItem *pPara; + + assert(pRun->type == diRun); + assert(pRun->member.run.nCharOfs != -1); + + pPara = ME_FindItemBack(pRun, diParagraph); + assert(pPara); + assert(pPara->type==diParagraph); + return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs + + ME_VPosToPos(pRun->member.run.strText, nOfs); +} + +void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor) +{ + ME_RunOfsFromCharOfs(editor, nCharOfs, &pCursor->pRun, &pCursor->nOffset); +} + +void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs) +{ + ME_DisplayItem *pPara; + int nParaOfs; + + pPara = editor->pBuffer->pFirst->member.para.next_para; + assert(pPara); + assert(ppRun); + assert(pOfs); + while (pPara->type == diParagraph) + { + nParaOfs = pPara->member.para.nCharOfs; + assert(nCharOfs >= nParaOfs); + + if (nCharOfs < pPara->member.para.next_para->member.para.nCharOfs) + { + *ppRun = ME_FindItemFwd(pPara, diRun); + assert(*ppRun); + while (!((*ppRun)->member.run.nFlags & MERF_ENDPARA)) + { + ME_DisplayItem *pNext = ME_FindItemFwd(*ppRun, diRun); + assert(pNext); + assert(pNext->type == diRun); + if (nCharOfs < nParaOfs + pNext->member.run.nCharOfs) { + *pOfs = ME_PosToVPos((*ppRun)->member.run.strText, + nCharOfs - nParaOfs - (*ppRun)->member.run.nCharOfs); + return; + } + *ppRun = pNext; + } + if (nCharOfs == nParaOfs + (*ppRun)->member.run.nCharOfs) { + *pOfs = 0; + return; + } + } + pPara = pPara->member.para.next_para; + } + *ppRun = ME_FindItemBack(editor->pBuffer->pLast, diRun); + *pOfs = 0; + assert((*ppRun)->member.run.nFlags & MERF_ENDPARA); +} + +void ME_JoinRuns(ME_TextEditor *editor, ME_DisplayItem *p) +{ + ME_DisplayItem *pNext = p->next; + int i; + assert(p->type == diRun && pNext->type == diRun); + assert(p->member.run.nCharOfs != -1); + + for (i=0; inCursors; i++) { + if (editor->pCursors[i].pRun == pNext) { + editor->pCursors[i].pRun = p; + editor->pCursors[i].nOffset += ME_StrVLen(p->member.run.strText); + } + } + + ME_AppendString(p->member.run.strText, pNext->member.run.strText); + ME_Remove(pNext); + ME_DestroyDisplayItem(pNext); + ME_UpdateRunFlags(editor, &p->member.run); + if(TRACE_ON(richedit)) + { + TRACE("Before check after join\n"); + ME_CheckCharOffsets(editor); + TRACE("After check after join\n"); + } +} + +ME_DisplayItem *ME_SplitRun(ME_Context *c, ME_DisplayItem *item, int nVChar) +{ + ME_TextEditor *editor = c->editor; + ME_DisplayItem *item2 = NULL; + ME_Run *run, *run2; + + assert(item->member.run.nCharOfs != -1); + if(TRACE_ON(richedit)) + { + TRACE("Before check before split\n"); + ME_CheckCharOffsets(editor); + TRACE("After check before split\n"); + } + + run = &item->member.run; + + TRACE("Before split: %s(%ld, %ld)\n", debugstr_w(run->strText->szData), + run->pt.x, run->pt.y); + + item2 = ME_SplitRunSimple(editor, item, nVChar); + + run2 = &item2->member.run; + + ME_CalcRunExtent(c, run); + ME_CalcRunExtent(c, run2); + + run2->pt.x = run->pt.x+run->nWidth; + run2->pt.y = run->pt.y; + + if(TRACE_ON(richedit)) + { + TRACE("Before check after split\n"); + ME_CheckCharOffsets(editor); + TRACE("After check after split\n"); + TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n", + debugstr_w(run->strText->szData), run->pt.x, run->pt.y, + debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y); + } + + return item2; +} + +/* split a run starting from voffset */ +ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar) +{ + ME_Run *run = &item->member.run; + ME_DisplayItem *item2; + ME_Run *run2; + int i; + assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText)); + assert(item->type == diRun); + assert(!(item->member.run.nFlags & MERF_GRAPHICS)); + assert(item->member.run.nCharOfs != -1); + + item2 = ME_MakeRun(run->style, + ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK); + + item2->member.run.nCharOfs = item->member.run.nCharOfs+ + ME_VPosToPos(item->member.run.strText, nVChar); + + run2 = &item2->member.run; + ME_InsertBefore(item->next, item2); + + ME_UpdateRunFlags(editor, run); + ME_UpdateRunFlags(editor, run2); + for (i=0; inCursors; i++) { + if (editor->pCursors[i].pRun == item && + editor->pCursors[i].nOffset >= nVChar) { + assert(item2->type == diRun); + editor->pCursors[i].pRun = item2; + editor->pCursors[i].nOffset -= nVChar; + } + } + ME_GetParagraph(item)->member.para.nFlags &= ~MEPF_WRAPPED; + return item2; +} + +/* split the start and final whitespace into separate runs */ +/* returns the last run added */ +/* +ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *item) +{ + int i, nVLen, nChanged; + assert(item->type == diRun); + assert(!(item->member.run.nFlags & MERF_GRAPHICS)); + return item; +} +*/ + +ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags) +{ + ME_DisplayItem *item = ME_MakeDI(diRun); + item->member.run.style = s; + item->member.run.strText = strData; + item->member.run.nFlags = nFlags; + item->member.run.nCharOfs = -1; + ME_AddRefStyle(s); + return item; +} + +ME_DisplayItem *ME_InsertRun(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem *pItem) +{ + ME_Cursor tmp; + ME_DisplayItem *pDI; + ME_UndoItem *pUI; + + assert(pItem->type == diRun || pItem->type == diUndoInsertRun); + + pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL); + pUI->nStart = nCharOfs; + pUI->nLen = pItem->member.run.strText->nLen; + ME_CursorFromCharOfs(editor, nCharOfs, &tmp); + if (tmp.nOffset) { + tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); + tmp.nOffset = 0; + } + pDI = ME_MakeRun(pItem->member.run.style, ME_StrDup(pItem->member.run.strText), pItem->member.run.nFlags); + pDI->member.run.nCharOfs = tmp.pRun->member.run.nCharOfs; + ME_InsertBefore(tmp.pRun, pDI); + TRACE("Shift length:%d\n", pDI->member.run.strText->nLen); + ME_PropagateCharOffset(tmp.pRun, pDI->member.run.strText->nLen); + ME_GetParagraph(tmp.pRun)->member.para.nFlags &= ~MEPF_WRAPPED; + + return pDI; +} + +static inline int ME_IsWSpace(WCHAR ch) +{ + return ch <= ' '; +} + +void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run) +{ + assert(run->nCharOfs != -1); + if (ME_IsSplitable(run->strText)) + run->nFlags |= MERF_SPLITTABLE; + else + run->nFlags &= ~MERF_SPLITTABLE; + + if (!(run->nFlags & MERF_GRAPHICS)) { + if (ME_IsWhitespaces(run->strText)) + run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE; + else + { + run->nFlags &= ~MERF_WHITESPACE; + + if (ME_IsWSpace(ME_GetCharFwd(run->strText,0))) + run->nFlags |= MERF_STARTWHITE; + else + run->nFlags &= ~MERF_STARTWHITE; + + if (ME_IsWSpace(ME_GetCharBack(run->strText,0))) + run->nFlags |= MERF_ENDWHITE; + else + run->nFlags &= ~MERF_ENDWHITE; + } + } + else + run->nFlags &= ~(MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE); +} + +void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize) +{ + assert(run->nFlags & MERF_GRAPHICS); + pSize->cx = 64; + pSize->cy = 64; +} + +int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run) +{ + int fit = 0; + HGDIOBJ hOldFont; + HDC hDC; + SIZE sz; + if (!run->strText->nLen) + return 0; + + if (run->nFlags & MERF_GRAPHICS) + { + SIZE sz; + ME_GetGraphicsSize(editor, run, &sz); + if (cx < sz.cx) + return 0; + return 1; + } + hDC = GetDC(editor->hWnd); + hOldFont = ME_SelectStyleFont(hDC, run->style); + GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen, + cx, &fit, NULL, &sz); + assert(run->style->hFont); + SelectObject(hDC, hOldFont); + ReleaseDC(editor->hWnd, hDC); + return fit; +} + +int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run) +{ + int fit = 0, fit1 = 0; + HGDIOBJ hOldFont; + HDC hDC; + SIZE sz, sz2, sz3; + if (!run->strText->nLen) + return 0; + + if (run->nFlags & MERF_GRAPHICS) + { + SIZE sz; + ME_GetGraphicsSize(editor, run, &sz); + if (cx < sz.cx/2) + return 0; + return 1; + } + + hDC = GetDC(editor->hWnd); + hOldFont = ME_SelectStyleFont(hDC, run->style); + GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen, + cx, &fit, NULL, &sz); + if (fit != run->strText->nLen) + { + int chars = 1; + + GetTextExtentPoint32W(hDC, run->strText->szData, fit, &sz2); + fit1 = ME_StrRelPos(run->strText, fit, &chars); + GetTextExtentPoint32W(hDC, run->strText->szData, fit1, &sz3); + if (cx >= (sz2.cx+sz3.cx)/2) + fit = fit1; + } + SelectObject(hDC, hOldFont); + ReleaseDC(editor->hWnd, hDC); + return fit; +} + +int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset) +{ + SIZE size; + HDC hDC = GetDC(editor->hWnd); + HGDIOBJ hOldFont; + + if (pRun->nFlags & MERF_GRAPHICS) + { + if (!nOffset) return 0; + ME_GetGraphicsSize(editor, pRun, &size); + return 1; + } + hOldFont = ME_SelectStyleFont(hDC, pRun->style); + GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size); + SelectObject(hDC, hOldFont); + ReleaseDC(editor->hWnd, hDC); + return size.cx; +} + +void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s, + SIZE *size) +{ + HDC hDC = c->hDC; + HGDIOBJ hOldFont; + hOldFont = ME_SelectStyleFont(hDC, s); + GetTextExtentPoint32W(hDC, szText, nChars, size); + SelectObject(hDC, hOldFont); +} + +SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen) +{ + SIZE size; + int nMaxLen = ME_StrVLen(run->strText); + + if (nLen>nMaxLen) + nLen = nMaxLen; + + if (run->nFlags & MERF_GRAPHICS) + { + ME_GetGraphicsSize(c->editor, run, &size); + return size; + } + + ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size); + + return size; +} + +void ME_CalcRunExtent(ME_Context *c, ME_Run *run) +{ + SIZE size; + int nEnd = ME_StrVLen(run->strText); + + if (run->nFlags & MERF_GRAPHICS) { + ME_GetGraphicsSize(c->editor, run, &size); + run->nWidth = size.cx; + run->nAscent = size.cy; + run->nDescent = 0; + return; + } + ME_GetTextExtent(c, run->strText->szData, nEnd, run->style, &size); + run->nWidth = size.cx; + run->nAscent = run->style->tm.tmAscent; + run->nDescent = run->style->tm.tmDescent; +} + +void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para) +{ + assert(para->type == diParagraph); + /* FIXME */ +} + +void ME_SetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) +{ + int nFrom, nTo; + ME_GetSelection(editor, &nFrom, &nTo); + if (nFrom == nTo) + { + ME_Style *s; + if (!editor->pBuffer->pCharStyle) + editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0); + s = ME_ApplyStyle(editor->pBuffer->pCharStyle, pFmt); + ME_ReleaseStyle(editor->pBuffer->pCharStyle); + editor->pBuffer->pCharStyle = s; + } + else + ME_SetCharFormat(editor, nFrom, nTo-nFrom, pFmt); +} + +void ME_SetCharFormat(ME_TextEditor *editor, int nOfs, int nChars, CHARFORMAT2W *pFmt) +{ + ME_Cursor tmp, tmp2; + ME_DisplayItem *para; + + ME_CursorFromCharOfs(editor, nOfs, &tmp); + if (tmp.nOffset) + tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); + + ME_CursorFromCharOfs(editor, nOfs+nChars, &tmp2); + if (tmp2.nOffset) + tmp2.pRun = ME_SplitRunSimple(editor, tmp2.pRun, tmp2.nOffset); + + para = ME_GetParagraph(tmp.pRun); + para->member.para.nFlags &= ~MEPF_WRAPPED; + + while(tmp.pRun != tmp2.pRun) + { + ME_UndoItem *undo = NULL; + ME_Style *new_style = ME_ApplyStyle(tmp.pRun->member.run.style, pFmt); + /* ME_DumpStyle(new_style); */ + undo = ME_AddUndoItem(editor, diUndoSetCharFormat, NULL); + if (undo) { + undo->nStart = tmp.pRun->member.run.nCharOfs+para->member.para.nCharOfs; + undo->nLen = tmp.pRun->member.run.strText->nLen; + undo->di.member.ustyle = tmp.pRun->member.run.style; + /* we'd have to addref undo..ustyle and release tmp...style + but they'd cancel each other out so we can do nothing instead */ + } + else + ME_ReleaseStyle(tmp.pRun->member.run.style); + tmp.pRun->member.run.style = new_style; + tmp.pRun = ME_FindItemFwd(tmp.pRun, diRunOrParagraph); + if (tmp.pRun->type == diParagraph) + { + para = tmp.pRun; + tmp.pRun = ME_FindItemFwd(tmp.pRun, diRun); + if (tmp.pRun != tmp2.pRun) + para->member.para.nFlags &= ~MEPF_WRAPPED; + } + assert(tmp.pRun); + } +} + +void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod) +{ + ME_Style *style; + ME_UndoItem *undo; + + assert(mod->cbSize == sizeof(CHARFORMAT2W)); + undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL); + if (undo) { + undo->nStart = -1; + undo->nLen = -1; + undo->di.member.ustyle = editor->pBuffer->pDefaultStyle; + ME_AddRefStyle(undo->di.member.ustyle); + } + style = ME_ApplyStyle(editor->pBuffer->pDefaultStyle, mod); + editor->pBuffer->pDefaultStyle->fmt = style->fmt; + editor->pBuffer->pDefaultStyle->tm = style->tm; + ME_ReleaseStyle(style); + ME_MarkAllForWrapping(editor); + /* pcf = editor->pBuffer->pDefaultStyle->fmt; */ +} + +void ME_GetRunCharFormat(ME_TextEditor *editor, ME_DisplayItem *run, CHARFORMAT2W *pFmt) +{ + ME_CopyCharFormat(pFmt, &run->member.run.style->fmt); +} + +void ME_GetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) +{ + int nFrom, nTo; + ME_GetSelection(editor, &nFrom, &nTo); + ME_CopyCharFormat(pFmt, &editor->pBuffer->pDefaultStyle->fmt); +} + +void ME_GetSelectionCharFormat(ME_TextEditor *editor, CHARFORMAT2W *pFmt) +{ + int nFrom, nTo; + ME_GetSelection(editor, &nFrom, &nTo); + if (nFrom == nTo && editor->pBuffer->pCharStyle) + { + ME_CopyCharFormat(pFmt, &editor->pBuffer->pCharStyle->fmt); + return; + } + ME_GetCharFormat(editor, nFrom, nTo, pFmt); +} + +void ME_GetCharFormat(ME_TextEditor *editor, int nFrom, int nTo, CHARFORMAT2W *pFmt) +{ + ME_DisplayItem *run, *run_end; + int nOffset, nOffset2; + CHARFORMAT2W tmp; + + if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */ + nTo--; + + ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset); + if (nFrom == nTo) /* special case - if selection is empty, take previous char's formatting */ + { + if (!nOffset) + { + ME_DisplayItem *tmp_run = ME_FindItemBack(run, diRunOrParagraph); + if (tmp_run->type == diRun) { + ME_GetRunCharFormat(editor, tmp_run, pFmt); + return; + } + } + ME_GetRunCharFormat(editor, run, pFmt); + return; + } + ME_RunOfsFromCharOfs(editor, nTo, &run_end, &nOffset2); + + ME_GetRunCharFormat(editor, run, pFmt); + + if (run == run_end) return; + + do { + /* FIXME add more style feature comparisons */ + int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR; + int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE; + + run = ME_FindItemFwd(run, diRun); + + ZeroMemory(&tmp, sizeof(tmp)); + tmp.cbSize = sizeof(tmp); + ME_GetRunCharFormat(editor, run, &tmp); + + assert((tmp.dwMask & nAttribs) == nAttribs); + assert((tmp.dwMask & nEffects) == nEffects); + /* reset flags that differ */ + + if (pFmt->yHeight != tmp.yHeight) + pFmt->dwMask &= ~CFM_SIZE; + if (pFmt->dwMask & CFM_FACE) + { + if (!(tmp.dwMask & CFM_FACE)) + pFmt->dwMask &= ~CFM_FACE; + else if (lstrcmpW(pFmt->szFaceName, tmp.szFaceName)) + pFmt->dwMask &= ~CFM_FACE; + } + if (pFmt->yHeight != tmp.yHeight) + pFmt->dwMask &= ~CFM_SIZE; + if (pFmt->dwMask & CFM_COLOR) + { + if (!((pFmt->dwEffects&CFE_AUTOCOLOR) & (tmp.dwEffects&CFE_AUTOCOLOR))) + { + if (pFmt->crTextColor != tmp.crTextColor) + pFmt->dwMask &= ~CFM_COLOR; + } + } + + pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects); + + } while(run != run_end); +} diff --git a/dlls/riched20/string.c b/dlls/riched20/string.c new file mode 100644 index 00000000000..c18fa1d5501 --- /dev/null +++ b/dlls/riched20/string.c @@ -0,0 +1,318 @@ +/* + * RichEdit - string operations + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +int ME_GetOptimalBuffer(int nLen) +{ + return ((2*nLen+1)+128)&~63; +} + +ME_String *ME_MakeString(LPCWSTR szText) +{ + ME_String *s = ALLOC_OBJ(ME_String); + s->nLen = lstrlenW(szText); + s->nBuffer = ME_GetOptimalBuffer(s->nLen+1); + s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer); + lstrcpyW(s->szData, szText); + return s; +} + +ME_String *ME_MakeStringN(LPCWSTR szText, int nMaxChars) +{ + ME_String *s = ALLOC_OBJ(ME_String); + int i; + for (i=0; inLen = i; + s->nBuffer = ME_GetOptimalBuffer(s->nLen+1); + s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer); + lstrcpynW(s->szData, szText, s->nLen+1); + return s; +} + +ME_String *ME_StrDup(ME_String *s) +{ + return ME_MakeStringN(s->szData, s->nLen); +} + +void ME_DestroyString(ME_String *s) +{ + FREE_OBJ(s->szData); + FREE_OBJ(s); +} + +void ME_AppendString(ME_String *s1, ME_String *s2) +{ + if (s1->nLen+s2->nLen+1 <= s1->nBuffer) { + lstrcpyW(s1->szData+s1->nLen, s2->szData); + s1->nLen += s2->nLen; + } + else + { + WCHAR *buf; + s1->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1); + + buf = ALLOC_N_OBJ(WCHAR, s1->nBuffer); + lstrcpyW(buf, s1->szData); + lstrcpyW(buf+s1->nLen, s2->szData); + FREE_OBJ(s1->szData); + s1->szData = buf; + s1->nLen += s2->nLen; + } +} + +ME_String *ME_ConcatString(ME_String *s1, ME_String *s2) +{ + ME_String *s = ALLOC_OBJ(ME_String); + s->nLen = s1->nLen+s2->nLen; + s->nBuffer = ME_GetOptimalBuffer(s1->nLen+s2->nLen+1); + s->szData = ALLOC_N_OBJ(WCHAR, s->nBuffer); + lstrcpyW(s->szData, s1->szData); + lstrcpyW(s->szData+s1->nLen, s2->szData); + return s; +} + +ME_String *ME_VSplitString(ME_String *orig, int charidx) +{ + ME_String *s; + + /*if (charidx<0) charidx = 0; + if (charidx>orig->nLen) charidx = orig->nLen; + */ + assert(charidx>=0); + assert(charidx<=orig->nLen); + + s = ME_MakeString(orig->szData+charidx); + orig->nLen = charidx; + orig->szData[charidx] = L'\0'; + return s; +} + +int ME_IsWhitespaces(ME_String *s) +{ + /* FIXME multibyte */ + WCHAR *pos = s->szData; + while(*pos++ == ' ') + ; + pos--; + if (*pos) + return 0; + else + return 1; +} + +int ME_IsSplitable(ME_String *s) +{ + /* FIXME multibyte */ + WCHAR *pos = s->szData; + WCHAR ch; + while(*pos++ == L' ') + ; + pos--; + while((ch = *pos++) != 0) + { + if (ch == L' ') + return 1; + } + return 0; +} + +/* FIXME multibyte */ +/* +int ME_CalcSkipChars(ME_String *s) +{ + int cnt = 0; + while(cnt < s->nLen && s->szData[s->nLen-1-cnt]==' ') + cnt++; + return cnt; +} +*/ + +int ME_StrLen(ME_String *s) { + return s->nLen; +} + +int ME_StrVLen(ME_String *s) { + return s->nLen; +} + +/* FIXME we use widechars, not multibytes, inside, no need for complex logic anymore */ +int ME_StrRelPos(ME_String *s, int nVChar, int *pRelChars) +{ + TRACE("%s,%d,&%d\n", debugstr_w(s->szData), nVChar, *pRelChars); + + assert(*pRelChars); + if (!*pRelChars) return nVChar; + + if (*pRelChars>0) + { + while(nVCharnLen && *pRelChars>0) + { + nVChar++; + (*pRelChars)--; + } + return nVChar; + } + + while(nVChar>0 && *pRelChars<0) + { + nVChar--; + (*pRelChars)++; + } + return nVChar; +} + +int ME_StrRelPos2(ME_String *s, int nVChar, int nRelChars) +{ + return ME_StrRelPos(s, nVChar, &nRelChars); +} + +int ME_VPosToPos(ME_String *s, int nVPos) +{ + return nVPos; + /* + int i = 0, len = 0; + if (!nVPos) + return 0; + while (i < s->nLen) + { + if (i == nVPos) + return len; + if (s->szData[i]=='\\') i++; + i++; + len++; + } + return len; + */ +} + +int ME_PosToVPos(ME_String *s, int nPos) +{ + if (!nPos) + return 0; + return ME_StrRelPos2(s, 0, nPos); +} + +void ME_StrDeleteV(ME_String *s, int nVChar, int nChars) +{ + int end_ofs; + + assert(nVChar >=0 && nVChar <= s->nLen); + assert(nChars >= 0); + assert(nVChar+nChars <= s->nLen); + + end_ofs = ME_StrRelPos2(s, nVChar, nChars); + assert(end_ofs <= s->nLen); + memmove(s->szData+nVChar, s->szData+end_ofs, 2*(s->nLen+1-end_ofs)); + s->nLen -= (end_ofs - nVChar); +} + +int ME_GetCharFwd(ME_String *s, int nPos) +{ + int nVPos = 0; + + assert(nPos < ME_StrLen(s)); + if (nPos) + nVPos = ME_StrRelPos2(s, nVPos, nPos); + + if (nVPos < s->nLen) + return s->szData[nVPos]; + return -1; +} + +int ME_GetCharBack(ME_String *s, int nPos) +{ + int nVPos = ME_StrVLen(s); + + assert(nPos < ME_StrLen(s)); + if (nPos) + nVPos = ME_StrRelPos2(s, nVPos, -nPos); + + if (nVPos < s->nLen) + return s->szData[nVPos]; + return -1; +} + +int ME_FindNonWhitespaceV(ME_String *s, int nVChar) { + int i; + for (i = nVChar; isspace(s->szData[i]) && inLen; i++) + ; + + return i; +} + +/* note: returns offset of the first trailing whitespace */ +int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar) { + int i; + for (i = nVChar; i>0 && isspace(s->szData[i-1]); i--) + ; + + return i; +} + +/* note: returns offset of the first trailing nonwhitespace */ +int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar) { + int i; + for (i = nVChar; i>0 && !isspace(s->szData[i-1]); i--) + ; + + return i; +} + +LPWSTR ME_ToUnicode(HWND hWnd, LPVOID psz) +{ + if (IsWindowUnicode(hWnd)) + return (LPWSTR)psz; + else { + WCHAR *tmp; + int nChars = MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, NULL, 0); + if((tmp = ALLOC_N_OBJ(WCHAR, nChars)) != NULL) + MultiByteToWideChar(CP_ACP, 0, (char *)psz, -1, tmp, nChars); + return tmp; + } +} + +void ME_EndToUnicode(HWND hWnd, LPVOID psz) +{ + if (IsWindowUnicode(hWnd)) + FREE_OBJ(psz); +} + +LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz) +{ + if (!IsWindowUnicode(hWnd)) + return (LPSTR)psz; + else { + char *tmp; + int nChars = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, NULL, 0, NULL, NULL); + if((tmp = ALLOC_N_OBJ(char, nChars)) != NULL) + WideCharToMultiByte(CP_ACP, 0, (WCHAR *)psz, -1, tmp, nChars, NULL, NULL); + return tmp; + } +} + +void ME_EndToAnsi(HWND hWnd, LPVOID psz) +{ + if (!IsWindowUnicode(hWnd)) + FREE_OBJ(psz); +} diff --git a/dlls/riched20/style.c b/dlls/riched20/style.c new file mode 100644 index 00000000000..09f23f2b4cc --- /dev/null +++ b/dlls/riched20/style.c @@ -0,0 +1,397 @@ +/* + * RichEdit style management functions + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +static int all_refs = 0; + +CHARFORMAT2W *ME_ToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from) +{ + if (from->cbSize == sizeof(CHARFORMATA)) + { + CHARFORMATA *f = (CHARFORMATA *)from; + CopyMemory(to, f, sizeof(*f)-sizeof(f->szFaceName)); + to->cbSize = sizeof(CHARFORMAT2W); + if (f->dwMask & CFM_FACE) { + MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)/sizeof(WCHAR)); + } + return to; + } + if (from->cbSize == sizeof(CHARFORMATW)) + { + CHARFORMATW *f = (CHARFORMATW *)from; + CopyMemory(to, f, sizeof(*f)); + /* theoretically, we don't need to zero the remaining memory */ + ZeroMemory(((CHARFORMATW *)to)+1, sizeof(CHARFORMAT2W)-sizeof(CHARFORMATW)); + to->cbSize = sizeof(CHARFORMAT2W); + return to; + } + if (from->cbSize == sizeof(CHARFORMAT2A)) + { + CHARFORMATA *f = (CHARFORMATA *)from; + /* copy the A structure without face name */ + CopyMemory(to, f, sizeof(CHARFORMATA)-sizeof(f->szFaceName)); + /* convert face name */ + if (f->dwMask & CFM_FACE) + MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName)); + /* copy the rest of the 2A structure to 2W */ + CopyMemory(1+((CHARFORMATW *)from), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA)); + to->cbSize = sizeof(CHARFORMAT2W); + return to; + } + + assert(from->cbSize >= sizeof(CHARFORMAT2W)); + return from; +} + +void ME_CopyToCF2W(CHARFORMAT2W *to, CHARFORMAT2W *from) +{ + if (ME_ToCF2W(to, from) == from) + CopyMemory(to, from, sizeof(*from)); +} + +CHARFORMAT2W *ME_ToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from) +{ + assert(from->cbSize == sizeof(CHARFORMAT2W)); + if (to->cbSize == sizeof(CHARFORMATA)) + { + CHARFORMATA *t = (CHARFORMATA *)to; + CopyMemory(t, from, sizeof(*t)-sizeof(t->szFaceName)); + WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0); + t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ + return to; + } + if (to->cbSize == sizeof(CHARFORMATW)) + { + CHARFORMATW *t = (CHARFORMATW *)to; + CopyMemory(t, from, sizeof(*t)); + t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ + return to; + } + if (to->cbSize == sizeof(CHARFORMAT2A)) + { + CHARFORMAT2A *t = (CHARFORMAT2A *)to; + /* copy the A structure without face name */ + CopyMemory(t, from, sizeof(CHARFORMATA)-sizeof(t->szFaceName)); + /* convert face name */ + WideCharToMultiByte(0, 0, from->szFaceName, -1, t->szFaceName, sizeof(t->szFaceName), 0, 0); + /* copy the rest of the 2A structure to 2W */ + CopyMemory(&t->wWeight, &from->wWeight, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA)); + t->cbSize = sizeof(*t); /* it was overwritten by CopyMemory */ + return to; + } + assert(to->cbSize >= sizeof(CHARFORMAT2W)); + return from; +} + +void ME_CopyToCFAny(CHARFORMAT2W *to, CHARFORMAT2W *from) +{ + if (ME_ToCFAny(to, from) == from) + CopyMemory(to, from, to->cbSize); +} + +ME_Style *ME_MakeStyle(CHARFORMAT2W *style) { + CHARFORMAT2W styledata; + ME_Style *s = ALLOC_OBJ(ME_Style); + + style = ME_ToCF2W(&styledata, style); + memset(s, 0, sizeof(ME_Style)); + if (style->cbSize <= sizeof(CHARFORMAT2W)) + CopyMemory(&s->fmt, style, style->cbSize); + else + CopyMemory(&s->fmt, style, sizeof(CHARFORMAT2W)); + s->fmt.cbSize = sizeof(CHARFORMAT2W); + + s->nSequence = -2; + s->nRefs = 1; + s->hFont = NULL; + all_refs++; + return s; +} + +#define COPY_STYLE_ITEM(mask, member) \ + if (style->dwMask & mask) { \ + s->fmt.dwMask |= mask;\ + s->fmt.member = style->member;\ + } + +#define COPY_STYLE_ITEM_MEMCPY(mask, member) \ + if (style->dwMask & mask) { \ + s->fmt.dwMask |= mask;\ + CopyMemory(s->fmt.member, style->member, sizeof(style->member));\ + } + +void ME_InitCharFormat2W(CHARFORMAT2W *pFmt) +{ + ZeroMemory(pFmt, sizeof(CHARFORMAT2W)); + pFmt->cbSize = sizeof(CHARFORMAT2W); +} + +ME_Style *ME_ApplyStyle(ME_Style *sSrc, CHARFORMAT2W *style) +{ + CHARFORMAT2W styledata; + ME_Style *s = ME_MakeStyle(&sSrc->fmt); + style = ME_ToCF2W(&styledata, style); + COPY_STYLE_ITEM(CFM_ANIMATION, bAnimation); + COPY_STYLE_ITEM(CFM_BACKCOLOR, crBackColor); + COPY_STYLE_ITEM(CFM_CHARSET, bCharSet); + COPY_STYLE_ITEM(CFM_COLOR, crTextColor); + COPY_STYLE_ITEM_MEMCPY(CFM_FACE, szFaceName); + COPY_STYLE_ITEM(CFM_KERNING, wKerning); + COPY_STYLE_ITEM(CFM_LCID, lcid); + COPY_STYLE_ITEM(CFM_OFFSET, yOffset); + COPY_STYLE_ITEM(CFM_REVAUTHOR, bRevAuthor); + COPY_STYLE_ITEM(CFM_SIZE, yHeight); + COPY_STYLE_ITEM(CFM_SPACING, sSpacing); + COPY_STYLE_ITEM(CFM_STYLE, sStyle); + COPY_STYLE_ITEM(CFM_UNDERLINETYPE, bUnderlineType); + COPY_STYLE_ITEM(CFM_WEIGHT, wWeight); + + s->fmt.dwEffects &= ~(style->dwMask); + s->fmt.dwEffects |= style->dwEffects & style->dwMask; + s->fmt.dwMask |= style->dwMask; + if (style->dwMask & CFM_COLOR) + { + if (style->dwEffects & CFE_AUTOCOLOR) + s->fmt.dwEffects |= CFE_AUTOCOLOR; + else + s->fmt.dwEffects &= ~CFE_AUTOCOLOR; + } + return s; +} + +void ME_CopyCharFormat(CHARFORMAT2W *pDest, CHARFORMAT2W *pSrc) +{ + /* using this with non-2W structs is forbidden */ + assert(pSrc->cbSize == sizeof(CHARFORMAT2W)); + assert(pDest->cbSize == sizeof(CHARFORMAT2W)); + CopyMemory(pDest, pSrc, sizeof(CHARFORMAT2W)); +} + +static void ME_DumpStyleEffect(char **p, const char *name, CHARFORMAT2W *fmt, int mask) +{ + *p += sprintf(*p, "%-22s%s\n", name, (fmt->dwMask & mask) ? ((fmt->dwEffects & mask) ? "YES" : "no") : "N/A"); +} + +void ME_DumpStyle(ME_Style *s) +{ + char buf[2048]; + ME_DumpStyleToBuf(&s->fmt, buf); + TRACE("%s\n", buf); +} + +void ME_DumpStyleToBuf(CHARFORMAT2W *pFmt, char buf[2048]) +{ + /* FIXME only CHARFORMAT styles implemented */ + /* this function sucks, doesn't check for buffer overruns but it's "good enough" as for debug code */ + char *p; + p = buf; + p += sprintf(p, "Font face: "); + if (pFmt->dwMask & CFM_FACE) { + WCHAR *q = pFmt->szFaceName; + while(*q) { + *p++ = (*q > 255) ? '?' : *q; + q++; + } + } else + p += sprintf(p, "N/A"); + + if (pFmt->dwMask & CFM_SIZE) + p += sprintf(p, "\nFont size: %d\n", (int)pFmt->yHeight); + else + p += sprintf(p, "\nFont size: N/A\n"); + + if (pFmt->dwMask & CFM_OFFSET) + p += sprintf(p, "Char offset: %d\n", (int)pFmt->yOffset); + else + p += sprintf(p, "Char offset: N/A\n"); + + if (pFmt->dwMask & CFM_CHARSET) + p += sprintf(p, "Font charset: %d\n", (int)pFmt->bCharSet); + else + p += sprintf(p, "Font charset: N/A\n"); + + /* I'm assuming CFM_xxx and CFE_xxx are the same values, fortunately it IS true wrt used flags*/ + ME_DumpStyleEffect(&p, "Font bold:", pFmt, CFM_BOLD); + ME_DumpStyleEffect(&p, "Font italic:", pFmt, CFM_ITALIC); + ME_DumpStyleEffect(&p, "Font underline:", pFmt, CFM_UNDERLINE); + ME_DumpStyleEffect(&p, "Font strikeout:", pFmt, CFM_STRIKEOUT); + p += sprintf(p, "Text color: "); + if (pFmt->dwMask & CFM_COLOR) + { + if (pFmt->dwEffects & CFE_AUTOCOLOR) + p += sprintf(p, "auto\n"); + else + p += sprintf(p, "%06x\n", (int)pFmt->crTextColor); + } + else + p += sprintf(p, "N/A\n"); + ME_DumpStyleEffect(&p, "Text protected:", pFmt, CFM_PROTECTED); +} + +void ME_UnprepareStyle(ME_Style *s) +{ + if (s->hFont) { + DeleteObject(s->hFont); + s->hFont = NULL; + s->nSequence = -2; + } +} + +void ME_PrepareStyleFromDC(ME_Style *s, HDC hDC, int nSequence) +{ + HGDIOBJ hOldFont; + LOGFONTW lf; + int rx, ry; + + if (nSequence == s->nSequence && s->hFont) + return; +/* assert(s); */ + rx = GetDeviceCaps(hDC, LOGPIXELSX); + ry = GetDeviceCaps(hDC, LOGPIXELSY); + if (s->hFont) { + DeleteObject(s->hFont); + s->hFont = NULL; + } + ZeroMemory(&lf, sizeof(lf)); + lstrcpyW(lf.lfFaceName, s->fmt.szFaceName); + lf.lfHeight = -s->fmt.yHeight*ry/1440; + lf.lfWeight = 400; + if (s->fmt.dwEffects & s->fmt.dwMask & CFM_BOLD) + lf.lfWeight = 700; + if (s->fmt.dwEffects & s->fmt.dwMask & CFM_WEIGHT) + lf.lfWeight = s->fmt.wWeight; + if (s->fmt.dwEffects & s->fmt.dwMask & CFM_ITALIC) + lf.lfItalic = 1; + if (s->fmt.dwEffects & s->fmt.dwMask & CFM_UNDERLINE) + lf.lfUnderline = 1; + if (s->fmt.dwEffects & s->fmt.dwMask & CFM_STRIKEOUT) + lf.lfStrikeOut = 1; +/*lf.lfQuality = PROOF_QUALITY; */ + lf.lfPitchAndFamily = s->fmt.bPitchAndFamily; + lf.lfCharSet = s->fmt.bCharSet; + s->hFont = CreateFontIndirectW(&lf); + assert(s->hFont); + GetObjectW(s->hFont, sizeof(LOGFONTW), &lf); + hOldFont = SelectObject(hDC, s->hFont); + GetTextMetricsW(hDC, &s->tm); + SelectObject(hDC, hOldFont); + s->nSequence = nSequence; +} + +HFONT ME_SelectStyleFont(HDC hDC, ME_Style *s) +{ + HFONT hOldFont; + ME_PrepareStyleFromDC(s, hDC, -1); + assert(s->hFont); + hOldFont = SelectObject(hDC, s->hFont); + return hOldFont; +} + +void ME_PrepareStyle(ME_Context *c, ME_Style *s) +{ + ME_PrepareStyleFromDC(s, c->hDC, c->nSequence); +} + +void ME_DestroyStyle(ME_Style *s) { + if (s->hFont) + { + DeleteObject(s->hFont); + s->hFont = NULL; + } + FREE_OBJ(s); +} + +void ME_AddRefStyle(ME_Style *s) +{ + assert(s->nRefs>0); /* style with 0 references isn't supposed to exist */ + s->nRefs++; + all_refs++; +} + +void ME_ReleaseStyle(ME_Style *s) +{ + s->nRefs--; + all_refs--; + if (s->nRefs==0) + TRACE("destroy style %p, total refs=%d\n", s, all_refs); + else + TRACE("release style %p, new refs=%d, total refs=%d\n", s, s->nRefs, all_refs); + if (!all_refs) FIXME("all style references freed (good!)\n"); + assert(s->nRefs>=0); + if (!s->nRefs) + ME_DestroyStyle(s); +} + +ME_Style *ME_GetInsertStyle(ME_TextEditor *editor, int nCursor) { + if (ME_IsSelection(editor)) + { + ME_Cursor c; + int from, to; + + ME_GetSelection(editor, &from, &to); + ME_CursorFromCharOfs(editor, from, &c); + ME_AddRefStyle(c.pRun->member.run.style); + return c.pRun->member.run.style; + } + if (editor->pBuffer->pCharStyle) { + ME_AddRefStyle(editor->pBuffer->pCharStyle); + return editor->pBuffer->pCharStyle; + } + else + { + ME_Cursor *pCursor = &editor->pCursors[nCursor]; + ME_DisplayItem *pRunItem = pCursor->pRun; + ME_DisplayItem *pPrevItem = NULL; + if (pCursor->nOffset) { + ME_Run *pRun = &pRunItem->member.run; + ME_AddRefStyle(pRun->style); + return pRun->style; + } + pPrevItem = ME_FindItemBack(pRunItem, diRunOrParagraph); + if (pPrevItem->type == diRun) + { + ME_AddRefStyle(pPrevItem->member.run.style); + return pPrevItem->member.run.style; + } + else + { + ME_AddRefStyle(pRunItem->member.run.style); + return pRunItem->member.run.style; + } + } +} + +void ME_SaveTempStyle(ME_TextEditor *editor) +{ + ME_Style *old_style = editor->pBuffer->pCharStyle; + editor->pBuffer->pCharStyle = ME_GetInsertStyle(editor, 0); + if (old_style) + ME_ReleaseStyle(old_style); +} + +void ME_ClearTempStyle(ME_TextEditor *editor) +{ + if (!editor->pBuffer->pCharStyle) return; + ME_ReleaseStyle(editor->pBuffer->pCharStyle); + editor->pBuffer->pCharStyle = NULL; +} diff --git a/dlls/riched20/undo.c b/dlls/riched20/undo.c new file mode 100644 index 00000000000..e95edc15d7e --- /dev/null +++ b/dlls/riched20/undo.c @@ -0,0 +1,261 @@ +/* + * RichEdit - functions dealing with editor object + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +void ME_EmptyUndoStack(ME_TextEditor *editor) +{ + ME_DisplayItem *p, *pNext; + + TRACE("Emptying undo stack\n"); + + p = editor->pUndoStack; + editor->pUndoStack = NULL; + while(p) { + pNext = p->next; + ME_DestroyDisplayItem(p); + p = pNext; + } + p = editor->pRedoStack; + editor->pRedoStack = NULL; + while(p) { + pNext = p->next; + ME_DestroyDisplayItem(p); + p = pNext; + } +} + +ME_UndoItem *ME_AddUndoItem(ME_TextEditor *editor, ME_DIType type, ME_DisplayItem *pdi) { + if (editor->nUndoMode == umIgnore) + return NULL; + else + { + ME_DisplayItem *pItem = (ME_DisplayItem *)ALLOC_OBJ(ME_UndoItem); + switch(type) + { + case diUndoEndTransaction: + break; + case diUndoSetParagraphFormat: + assert(pdi); + CopyMemory(&pItem->member.para, &pdi->member.para, sizeof(ME_Paragraph)); + pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); + CopyMemory(pItem->member.para.pFmt, pdi->member.para.pFmt, sizeof(PARAFORMAT2)); + break; + case diUndoInsertRun: + assert(pdi); + CopyMemory(&pItem->member.run, &pdi->member.run, sizeof(ME_Run)); + pItem->member.run.strText = ME_StrDup(pItem->member.run.strText); + ME_AddRefStyle(pItem->member.run.style); + break; + case diUndoSetCharFormat: + case diUndoSetDefaultCharFormat: + break; + case diUndoDeleteRun: + case diUndoJoinParagraphs: + break; + case diUndoSplitParagraph: + pItem->member.para.pFmt = ALLOC_OBJ(PARAFORMAT2); + pItem->member.para.pFmt->cbSize = sizeof(PARAFORMAT2); + pItem->member.para.pFmt->dwMask = 0; + + break; + default: + assert(0 == "AddUndoItem, unsupported item type"); + return NULL; + } + pItem->type = type; + pItem->prev = NULL; + if (editor->nUndoMode == umAddToUndo || editor->nUndoMode == umAddBackToUndo) + { + if (editor->nUndoMode == umAddToUndo) + TRACE("Pushing id=%s to undo stack, deleting redo stack\n", ME_GetDITypeName(type)); + else + TRACE("Pushing id=%s to undo stack\n", ME_GetDITypeName(type)); + pItem->next = editor->pUndoStack; + if (editor->pUndoStack) + editor->pUndoStack->prev = pItem; + editor->pUndoStack = pItem; + /* any new operation (not redo) clears the redo stack */ + if (editor->nUndoMode == umAddToUndo) { + ME_DisplayItem *p = editor->pRedoStack; + while(p) + { + ME_DisplayItem *pp = p->next; + ME_DestroyDisplayItem(p); + p = pp; + } + editor->pRedoStack = NULL; + } + } + else if (editor->nUndoMode == umAddToRedo) + { + TRACE("Pushing id=%s to redo stack\n", ME_GetDITypeName(type)); + pItem->next = editor->pRedoStack; + if (editor->pRedoStack) + editor->pRedoStack->prev = pItem; + editor->pRedoStack = pItem; + } + else + assert(0); + return (ME_UndoItem *)pItem; + } +} + +void ME_CommitUndo(ME_TextEditor *editor) { + assert(editor->nUndoMode == umAddToUndo); + + /* no transactions, no need to commit */ + if (!editor->pUndoStack) + return; + + /* no need to commit empty transactions */ + if (editor->pUndoStack->type == diUndoEndTransaction) + return; + + ME_AddUndoItem(editor, diUndoEndTransaction, NULL); + ME_SendSelChange(editor); + editor->nModifyStep++; +} + +void ME_PlayUndoItem(ME_TextEditor *editor, ME_DisplayItem *pItem) +{ + ME_UndoItem *pUItem = (ME_UndoItem *)pItem; + + TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type)); + + switch(pItem->type) + { + case diUndoEndTransaction: + assert(0); + case diUndoSetParagraphFormat: + { + ME_Cursor tmp; + ME_CursorFromCharOfs(editor, pItem->member.para.nCharOfs, &tmp); + ME_SetParaFormat(editor, ME_FindItemBack(tmp.pRun, diParagraph), pItem->member.para.pFmt); + break; + } + case diUndoSetCharFormat: + { + ME_SetCharFormat(editor, pUItem->nStart, pUItem->nLen, &pItem->member.ustyle->fmt); + break; + } + case diUndoSetDefaultCharFormat: + { + ME_SetDefaultCharFormat(editor, &pItem->member.ustyle->fmt); + break; + } + case diUndoInsertRun: + { + ME_InsertRun(editor, pItem->member.run.nCharOfs, pItem); + break; + } + case diUndoDeleteRun: + { + ME_InternalDeleteText(editor, pUItem->nStart, pUItem->nLen); + break; + } + case diUndoJoinParagraphs: + { + ME_Cursor tmp; + ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); + /* the only thing that's needed is paragraph offset, so no need to split runs */ + ME_JoinParagraphs(editor, ME_GetParagraph(tmp.pRun)); + break; + } + case diUndoSplitParagraph: + { + ME_Cursor tmp; + ME_DisplayItem *new_para; + ME_CursorFromCharOfs(editor, pUItem->nStart, &tmp); + if (tmp.nOffset) + tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset); + new_para = ME_SplitParagraph(editor, tmp.pRun, tmp.pRun->member.run.style); + assert(pItem->member.para.pFmt->cbSize == sizeof(PARAFORMAT2)); + CopyMemory(new_para->member.para.pFmt, pItem->member.para.pFmt, sizeof(PARAFORMAT2)); + break; + } + default: + assert(0 == "PlayUndoItem, unexpected type"); + } +} + +void ME_Undo(ME_TextEditor *editor) { + ME_DisplayItem *p; + ME_UndoMode nMode = editor->nUndoMode; + + assert(nMode == umAddToUndo || nMode == umIgnore); + + /* no undo items ? */ + if (!editor->pUndoStack) + return; + + /* watch out for uncommited transactions ! */ + assert(editor->pUndoStack->type == diUndoEndTransaction); + + editor->nUndoMode = umAddToRedo; + p = editor->pUndoStack->next; + ME_DestroyDisplayItem(editor->pUndoStack); + do { + ME_DisplayItem *pp = p; + ME_PlayUndoItem(editor, p); + p = p->next; + ME_DestroyDisplayItem(pp); + } while(p && p->type != diUndoEndTransaction); + ME_AddUndoItem(editor, diUndoEndTransaction, NULL); + editor->pUndoStack = p; + if (p) + p->prev = NULL; + editor->nUndoMode = nMode; + editor->nModifyStep--; + ME_UpdateRepaint(editor); +} + +void ME_Redo(ME_TextEditor *editor) { + ME_DisplayItem *p; + ME_UndoMode nMode = editor->nUndoMode; + + assert(nMode == umAddToUndo || nMode == umIgnore); + + /* no redo items ? */ + if (!editor->pRedoStack) + return; + + /* watch out for uncommited transactions ! */ + assert(editor->pRedoStack->type == diUndoEndTransaction); + + editor->nUndoMode = umAddBackToUndo; + p = editor->pRedoStack->next; + ME_DestroyDisplayItem(editor->pRedoStack); + do { + ME_DisplayItem *pp = p; + ME_PlayUndoItem(editor, p); + p = p->next; + ME_DestroyDisplayItem(pp); + } while(p && p->type != diUndoEndTransaction); + ME_AddUndoItem(editor, diUndoEndTransaction, NULL); + editor->pRedoStack = p; + if (p) + p->prev = NULL; + editor->nUndoMode = nMode; + editor->nModifyStep++; + ME_UpdateRepaint(editor); +} diff --git a/dlls/riched20/wrap.c b/dlls/riched20/wrap.c new file mode 100644 index 00000000000..6ec16b4362a --- /dev/null +++ b/dlls/riched20/wrap.c @@ -0,0 +1,415 @@ +/* + * RichEdit - Paragraph wrapping. Don't try to understand it. You've been + * warned ! + * + * Copyright 2004 by Krzysztof Foltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + +/* + * Unsolved problems: + * + * - center and right align in WordPad omits all spaces at the start, we don't + * - objects/images are not handled yet + * - no tabs + */ + +ME_DisplayItem *ME_MakeRow(int height, int baseline, int width) +{ + ME_DisplayItem *item = ME_MakeDI(diStartRow); + + item->member.row.nHeight = height; + item->member.row.nBaseline = baseline; + item->member.row.nWidth = width; + return item; +} + +void ME_BeginRow(ME_WrapContext *wc) +{ + wc->pRowStart = NULL; + wc->bOverflown = FALSE; + wc->pLastSplittableRun = NULL; + wc->nAvailWidth = wc->nTotalWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin; + wc->pt.x = 0; +} + +void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd) +{ + ME_DisplayItem *p, *row, *para; + int ascent = 0, descent = 0, width=0, shift = 0, align = 0; + /* wrap text */ + para = ME_GetParagraph(wc->pRowStart); + for (p = wc->pRowStart; p!=pEnd; p = p->next) + { + /* ENDPARA run shouldn't affect row height, except if it's the only run in the paragraph */ + if (p->type==diRun && ((p==wc->pRowStart) || !(p->member.run.nFlags & MERF_ENDPARA))) { /* FIXME add more run types */ + if (p->member.run.nAscent>ascent) + ascent = p->member.run.nAscent; + if (p->member.run.nDescent>descent) + descent = p->member.run.nDescent; + if (!(p->member.run.nFlags & (MERF_ENDPARA|MERF_SKIPPED))) + width += p->member.run.nWidth; + } + } + row = ME_MakeRow(ascent+descent, ascent, width); + row->member.row.nYPos = wc->pt.y; + row->member.row.nLMargin = (!wc->nRow ? wc->nFirstMargin : wc->nLeftMargin); + row->member.row.nRMargin = wc->nRightMargin; + assert(para->member.para.pFmt->dwMask & PFM_ALIGNMENT); + align = para->member.para.pFmt->wAlignment; + if (align == PFA_CENTER) + shift = (wc->nAvailWidth-width)/2; + if (align == PFA_RIGHT) + shift = wc->nAvailWidth-width; + for (p = wc->pRowStart; p!=pEnd; p = p->next) + { + if (p->type==diRun) { /* FIXME add more run types */ + p->member.run.pt.x += row->member.row.nLMargin+shift; + } + } + ME_InsertBefore(wc->pRowStart, row); + wc->nRow++; + wc->pt.y += ascent+descent; + ME_BeginRow(wc); +} + +void ME_WrapEndParagraph(ME_WrapContext *wc, ME_DisplayItem *p) +{ + if (wc->pRowStart) + ME_InsertRowStart(wc, p->next); + + /* + p = p->member.para.prev_para->next; + while(p) { + if (p->type == diParagraph || p->type == diTextEnd) + return; + if (p->type == diRun) + { + ME_Run *run = &p->member.run; + TRACE("%s - (%d, %d)\n", debugstr_w(run->strText->szData), run->pt.x, run->pt.y); + } + p = p->next; + } + */ +} + +void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p) +{ + /* FIXME compose style (out of character and paragraph styles) here */ + + ME_UpdateRunFlags(wc->context->editor, &p->member.run); + + ME_CalcRunExtent(wc->context, &p->member.run); +} + +ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i) +{ + ME_DisplayItem *pp, *piter = p; + int j; + if (!i) + return NULL; + j = ME_ReverseFindNonWhitespaceV(p->member.run.strText, i); + if (j>0) { + pp = ME_SplitRun(wc->context, piter, j); + wc->pt.x += piter->member.run.nWidth; + return pp; + } + else + { + pp = piter; + /* omit all spaces before split point */ + while(piter != wc->pRowStart) + { + piter = ME_FindItemBack(piter, diRun); + if (piter->member.run.nFlags & MERF_WHITESPACE) + { + pp = piter; + continue; + } + if (piter->member.run.nFlags & MERF_ENDWHITE) + { + j = ME_ReverseFindNonWhitespaceV(piter->member.run.strText, i); + pp = ME_SplitRun(wc->context, piter, i); + wc->pt = pp->member.run.pt; + return pp; + } + /* this run is the end of spaces, so the run edge is a good point to split */ + wc->pt = pp->member.run.pt; + wc->bOverflown = TRUE; + TRACE("Split point is: %s|%s\n", debugstr_w(piter->member.run.strText->szData), debugstr_w(pp->member.run.strText->szData)); + return pp; + } + wc->pt = piter->member.run.pt; + return piter; + } +} + +ME_DisplayItem *ME_SplitByBacktracking(ME_WrapContext *wc, ME_DisplayItem *p, int loc) +{ + ME_DisplayItem *piter = p, *pp; + int i, idesp, len; + ME_Run *run = &p->member.run; + + idesp = i = ME_CharFromPoint(wc->context->editor, loc, run); + len = ME_StrVLen(run->strText); + assert(len>0); + assert(istrText, i); + pp = ME_MaximizeSplit(wc, p, i); + if (pp) + return pp; + } + TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData)); + if (wc->pLastSplittableRun) + { + if (wc->pLastSplittableRun->member.run.nFlags & MERF_GRAPHICS) + { + wc->pt = wc->ptLastSplittableRun; + return wc->pLastSplittableRun; + } + else if (wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE) + { + /* the following two lines are just to check if we forgot to call UpdateRunFlags earlier, + they serve no other purpose */ + ME_UpdateRunFlags(wc->context->editor, run); + assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE)); + + piter = wc->pLastSplittableRun; + run = &piter->member.run; + len = ME_StrVLen(run->strText); + /* don't split words */ + i = ME_ReverseFindWhitespaceV(run->strText, len); + if (i == len) + i = ME_ReverseFindNonWhitespaceV(run->strText, len); + if (i) { + ME_DisplayItem *piter2 = ME_SplitRun(wc->context, piter, i); + wc->pt = piter2->member.run.pt; + return piter2; + } + /* splittable = must have whitespaces */ + assert(0 == "Splittable, but no whitespaces"); + } + else + { + /* restart from the first run beginning with spaces */ + wc->pt = wc->ptLastSplittableRun; + return wc->pLastSplittableRun; + } + } + TRACE("Backtracking failed, trying desperate: %s\n", debugstr_w(p->member.run.strText->szData)); + /* OK, no better idea, so assume we MAY split words if we can split at all*/ + if (idesp) + return ME_SplitRun(wc->context, piter, idesp); + else + if (wc->pRowStart && piter != wc->pRowStart) + { + /* don't need to break current run, because it's possible to split + before this run */ + wc->bOverflown = TRUE; + return piter; + } + else + { + /* split point inside first character - no choice but split after that char */ + int chars = 1; + int pos2 = ME_StrRelPos(run->strText, 0, &chars); + if (pos2 != len) { + /* the run is more than 1 char, so we may split */ + return ME_SplitRun(wc->context, piter, pos2); + } + /* the run is one char, can't split it */ + return piter; + } +} + +ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p) +{ + ME_DisplayItem *pp; + ME_Run *run; + int len; + + assert(p->type == diRun); + if (!wc->pRowStart) + wc->pRowStart = p; + ME_WrapSizeRun(wc, p); + run = &p->member.run; + run->pt.x = wc->pt.x; + run->pt.y = wc->pt.y; + len = ME_StrVLen(run->strText); + + if (wc->bOverflown) /* just skipping final whitespaces */ + { + if (run->nFlags & MERF_WHITESPACE) { + p->member.run.nFlags |= MERF_SKIPPED; + /* wc->pt.x += run->nWidth; */ + /* skip runs consisting of only whitespaces */ + return p->next; + } + + if (run->nFlags & MERF_STARTWHITE) { + /* try to split the run at the first non-white char */ + int black; + black = ME_FindNonWhitespaceV(run->strText, 0); + if (black) { + wc->bOverflown = FALSE; + pp = ME_SplitRun(wc->context, p, black); + p->member.run.nFlags |= MERF_SKIPPED; +/* + run->pt = wc->pt; + wc->pt.x += run->nWidth; + */ + ME_InsertRowStart(wc, pp); + return pp; + } + } + /* black run: the row goes from pRowStart to the previous run */ + ME_InsertRowStart(wc, p); + return p; + } + /* we're not at the end of the row */ + /* will current run fit? */ + if (wc->pt.x + run->nWidth > wc->nAvailWidth) + { + int loc = wc->nAvailWidth - wc->pt.x; + /* total white run ? */ + if (run->nFlags & MERF_WHITESPACE) { + /* let the overflow logic handle it */ + wc->bOverflown = TRUE; + return p; + } + /* graphics - we can split before */ + if (run->nFlags & MERF_GRAPHICS) { + wc->bOverflown = TRUE; + return p; + } + /* can we separate out the last spaces ? (to use overflow logic later) */ + if (run->nFlags & MERF_ENDWHITE) + { + /* we aren't sure if it's *really* necessary, it's a good start however */ + int black = ME_ReverseFindNonWhitespaceV(run->strText, len); + ME_SplitRun(wc->context, p, black); + /* handle both parts again */ + return p; + } + /* determine the split point by backtracking */ + pp = ME_SplitByBacktracking(wc, p, loc); + if (pp == wc->pRowStart) + { + /* we had only spaces so far, entire content can be omitted */ + wc->pt.x = 0; + return p->next; + } + if (p != pp) /* found a suitable split point */ + { + wc->bOverflown = TRUE; + return pp; + } + /* we detected that it's best to split on start of this run */ + if (wc->bOverflown) + return pp; + ERR("failure!\n"); + /* not found anything - writing over margins is the only option left */ + } + if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE)) + || ((run->nFlags & MERF_GRAPHICS) && (p != wc->pRowStart))) + { + wc->pLastSplittableRun = p; + wc->ptLastSplittableRun = wc->pt; + } + wc->pt.x += run->nWidth; + return p->next; +} + +void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) { + ME_DisplayItem *p; + ME_WrapContext wc; + + assert(tp->type == diParagraph); + if (tp->member.para.nFlags & MEPF_WRAPPED) { + return; + } + ME_PrepareParagraphForWrapping(c, tp); + + wc.context = c; +/* wc.para_style = tp->member.para.style; */ + wc.style = NULL; + wc.nFirstMargin = tp->member.para.nFirstMargin; + wc.nLeftMargin = tp->member.para.nLeftMargin; + wc.nRightMargin = tp->member.para.nRightMargin; + wc.nRow = 0; + wc.pt.x = 0; + wc.pt.y = 0; + wc.nTotalWidth = c->rcView.right - c->rcView.left; + wc.nAvailWidth = wc.nTotalWidth - wc.nFirstMargin - wc.nRightMargin; + wc.pRowStart = NULL; + + ME_BeginRow(&wc); + for (p = tp->next; p!=tp->member.para.next_para; ) { + assert(p->type != diStartRow); + if (p->type == diRun) { + ME_PrepareStyle(c, p->member.run.style); + p = ME_WrapHandleRun(&wc, p); + continue; + } + p = p->next; + } + ME_WrapEndParagraph(&wc, p); + tp->member.para.nFlags |= MEPF_WRAPPED; + tp->member.para.nHeight = wc.pt.y; +} + + +void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) { + ME_DisplayItem *p; + /* remove all items that will be reinserted by paragraph wrapper anyway */ + for (p = tp->next; p!=tp->member.para.next_para; p = p->next) { + switch(p->type) { + case diStartRow: + p = p->prev; + ME_Remove(p->next); + break; + default: + break; + } + } + /* join runs that can be joined, set up flags */ + for (p = tp->next; p!=tp->member.para.next_para; p = p->next) { + int changed = 0; + switch(p->type) { + case diStartRow: assert(0); break; /* should have deleted it */ + case diRun: + while (p->next->type == diRun) { /* FIXME */ + if (ME_CanJoinRuns(&p->member.run, &p->next->member.run)) { + ME_JoinRuns(c->editor, p); + changed = 1; + } + else + break; + } + p->member.run.nFlags &= ~MERF_CALCBYWRAP; + break; + default: + break; + } + } +}