summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
Diffstat (limited to 'win')
-rw-r--r--win/README124
-rw-r--r--win/makefile.bc341
-rw-r--r--win/makefile.vc397
-rw-r--r--win/rc/buttons.bmp0
-rw-r--r--win/rc/cursor00.cur0
-rw-r--r--win/rc/cursor02.cur0
-rw-r--r--win/rc/cursor04.cur0
-rw-r--r--win/rc/cursor06.cur0
-rw-r--r--win/rc/cursor08.cur0
-rw-r--r--win/rc/cursor0a.cur0
-rw-r--r--win/rc/cursor0c.cur0
-rw-r--r--win/rc/cursor0e.cur0
-rw-r--r--win/rc/cursor10.cur0
-rw-r--r--win/rc/cursor12.cur0
-rw-r--r--win/rc/cursor14.cur0
-rw-r--r--win/rc/cursor16.cur0
-rw-r--r--win/rc/cursor18.cur0
-rw-r--r--win/rc/cursor1a.cur0
-rw-r--r--win/rc/cursor1c.cur0
-rw-r--r--win/rc/cursor1e.cur0
-rw-r--r--win/rc/cursor20.cur2
-rw-r--r--win/rc/cursor22.cur0
-rw-r--r--win/rc/cursor24.cur2
-rw-r--r--win/rc/cursor26.cur0
-rw-r--r--win/rc/cursor28.cur0
-rw-r--r--win/rc/cursor2a.cur0
-rw-r--r--win/rc/cursor2c.cur0
-rw-r--r--win/rc/cursor2e.cur0
-rw-r--r--win/rc/cursor30.cur0
-rw-r--r--win/rc/cursor32.cur0
-rw-r--r--win/rc/cursor34.cur0
-rw-r--r--win/rc/cursor36.cur0
-rw-r--r--win/rc/cursor38.cur0
-rw-r--r--win/rc/cursor3a.cur0
-rw-r--r--win/rc/cursor3c.cur0
-rw-r--r--win/rc/cursor3e.cur0
-rw-r--r--win/rc/cursor40.cur0
-rw-r--r--win/rc/cursor42.cur0
-rw-r--r--win/rc/cursor44.cur0
-rw-r--r--win/rc/cursor46.cur0
-rw-r--r--win/rc/cursor48.cur0
-rw-r--r--win/rc/cursor4a.cur0
-rw-r--r--win/rc/cursor4c.cur0
-rw-r--r--win/rc/cursor4e.cur0
-rw-r--r--win/rc/cursor50.cur0
-rw-r--r--win/rc/cursor52.cur0
-rw-r--r--win/rc/cursor54.cur0
-rw-r--r--win/rc/cursor56.cur0
-rw-r--r--win/rc/cursor58.cur0
-rw-r--r--win/rc/cursor5a.cur0
-rw-r--r--win/rc/cursor5c.cur0
-rw-r--r--win/rc/cursor5e.cur1
-rw-r--r--win/rc/cursor60.cur0
-rw-r--r--win/rc/cursor62.cur0
-rw-r--r--win/rc/cursor64.cur0
-rw-r--r--win/rc/cursor66.cur0
-rw-r--r--win/rc/cursor68.cur0
-rw-r--r--win/rc/cursor6a.cur0
-rw-r--r--win/rc/cursor6c.cur0
-rw-r--r--win/rc/cursor6e.cur0
-rw-r--r--win/rc/cursor70.cur0
-rw-r--r--win/rc/cursor72.cur0
-rw-r--r--win/rc/cursor74.cur0
-rw-r--r--win/rc/cursor76.cur0
-rw-r--r--win/rc/cursor78.cur1
-rw-r--r--win/rc/cursor7a.cur0
-rw-r--r--win/rc/cursor7c.cur0
-rw-r--r--win/rc/cursor7e.cur0
-rw-r--r--win/rc/cursor80.cur0
-rw-r--r--win/rc/cursor82.cur0
-rw-r--r--win/rc/cursor84.cur0
-rw-r--r--win/rc/cursor86.cur0
-rw-r--r--win/rc/cursor88.cur0
-rw-r--r--win/rc/cursor8a.cur0
-rw-r--r--win/rc/cursor8c.cur0
-rw-r--r--win/rc/cursor8e.cur0
-rw-r--r--win/rc/cursor90.cur0
-rw-r--r--win/rc/cursor92.cur0
-rw-r--r--win/rc/cursor94.cur0
-rw-r--r--win/rc/cursor96.cur0
-rw-r--r--win/rc/cursor98.cur0
-rw-r--r--win/rc/tk.ico0
-rw-r--r--win/rc/tk.rc132
-rw-r--r--win/rc/wish.ico0
-rw-r--r--win/rc/wish.rc44
-rw-r--r--win/stubs.c397
-rw-r--r--win/tkWin.h56
-rw-r--r--win/tkWin32Dll.c85
-rw-r--r--win/tkWin3d.c535
-rw-r--r--win/tkWinButton.c811
-rw-r--r--win/tkWinClipboard.c291
-rw-r--r--win/tkWinColor.c615
-rw-r--r--win/tkWinCursor.c210
-rw-r--r--win/tkWinDefault.h456
-rw-r--r--win/tkWinDialog.c1050
-rw-r--r--win/tkWinDraw.c1264
-rw-r--r--win/tkWinEmbed.c645
-rw-r--r--win/tkWinFont.c643
-rw-r--r--win/tkWinImage.c329
-rw-r--r--win/tkWinInit.c121
-rw-r--r--win/tkWinInt.h194
-rw-r--r--win/tkWinKey.c360
-rw-r--r--win/tkWinMenu.c2646
-rw-r--r--win/tkWinPixmap.c184
-rw-r--r--win/tkWinPointer.c457
-rw-r--r--win/tkWinPort.h117
-rw-r--r--win/tkWinRegion.c179
-rw-r--r--win/tkWinScrlbr.c745
-rw-r--r--win/tkWinSend.c86
-rw-r--r--win/tkWinWindow.c796
-rw-r--r--win/tkWinWm.c4115
-rw-r--r--win/tkWinX.c1020
-rw-r--r--win/winMain.c323
113 files changed, 19774 insertions, 0 deletions
diff --git a/win/README b/win/README
new file mode 100644
index 0000000..17a488c
--- /dev/null
+++ b/win/README
@@ -0,0 +1,124 @@
+Tk 8.0p2 for Windows
+
+by Scott Stanton
+Sun Microsystems Laboratories
+scott.stanton@eng.sun.com
+
+SCCS: @(#) README 1.20 97/11/21 15:17:54
+
+1. Introduction
+---------------
+
+This is the directory where you configure and compile the Windows
+version of Tk. This directory also contains source files for Tk
+that are specific to Microsoft Windows. The rest of this file
+contains information specific to the Windows version of Tk.
+
+2. Distribution notes
+---------------------
+
+Tk 8.0 for Windows is distributed in binary form in addition to the
+common source release. The binary distribution is a self-extracting
+archive with a built-in installation script.
+
+Look for the binary release in the same location as the source release
+(ftp.smli.com:/pub/tcl or any of the mirror sites). For most users,
+the binary release will be much easier to install and use. You only
+need the source release if you plan to modify the core of Tcl, or if
+you need to compile with a different compiler. With the addition of
+the dynamic loading interface, it is no longer necessary to have the
+source distribution in order to build and use extensions.
+
+3. Compiling Tk
+----------------
+
+In order to compile Tk for Windows, you need the following items:
+
+ Tcl 8.0 Source Distribution (plus any patches)
+ Tk 8.0 Source Distribution (plus any patches)
+
+ The latest Win32 SDK header files
+
+ Borland C++ 4.5 or later (32-bit compiler)
+ or
+ Visual C++ 2.x or later
+
+
+In the "win" subdirectory of the source release, you will find two
+files called "makefile.bc" and "makefile.vc". These are the makefiles
+for the Borland and Visual C++ compilers respectively. You should
+copy the appropriate one to "makefile" and update the paths at the top
+of the file to reflect your system configuration. Now you can use
+"make" (or "nmake" for VC++) to build the tk libraries and the wish
+executable.
+
+In order to use the binaries generated by these makefiles, you will
+need to place the Tk script library files someplace where Tk can
+find them. Tk looks in one of two places for the library files:
+
+ 1) The environment variable "TK_LIBRARY".
+
+ 2) In the lib\tk8.0 directory under the Tcl installation directory
+ as specified in the registry:
+
+ For Windows NT & 95:
+ HKEY_LOCAL_MACHINE\SOFTWARE\Sun\Tcl\8.0
+ Value Name is "Root"
+
+ For Win32s:
+ HKEY_CLASSES_ROOT\SOFTWARE\Sun\Tcl\8.0\
+
+ 2) Relative to the directory containing the current .exe.
+ Tk will look for a directory "..\lib\tk8.0" relative to the
+ directory containing the currently running .exe.
+
+Note that in order to run wish80.exe, you must ensure that tcl80.dll,
+tclpip80.dll (plus tcl1680.dll under Win32s), and tk80.dll are on your
+path, in the system directory, or in the directory containing
+wish80.exe.
+
+4. Test suite
+-------------
+
+The Windows version of Tk does not pass many of the tests in the test
+suite. This is primarily due to dependencies in the test suite on the
+size of particular X fonts, and other X related features as well as
+problems with "exec". We will be working to develop a more general
+test suite for Tk under Windows, but for now, you will not be able to
+pass many of the tests.
+
+5. Known Bugs
+-------------
+
+Here is the current list of known bugs/missing features for the
+Windows beta version of Tk:
+
+- There is no support for custom cursors/application icons. The core
+ set of X cursors is supported, although you cannot change their color.
+- Stippling of arcs isn't implemented yet.
+- Some "wm" functions don't map to Windows and aren't implemented;
+ others should map, but just aren't implemented. The worst offenders
+ are the icon manipulation routines.
+- Under Win32s, you can only start one instance of Wish at a time.
+- Color management on some displays doesn't work properly resulting in
+ Tk switching to monochrome mode.
+- Tk seems to fail to draw anything on some Matrox Millenium cards.
+- Send and winfo interps are not currently supported
+- Printing does not work for images (e.g. GIF) on a canvas.
+- Tk_dialog appears in the upper left corner. This is a symptom of a
+ larger problem with "wm geometry" when applied to unmapped or
+ iconified windows.
+- Some keys don't work on international keyboards.
+- Grabs do not affect native menus or the title bar.
+- PPM images are using the wrong translation mode for writing to
+ files, resulting in CR/LF terminated PPM files.
+- Tk crashes if the display depth changes while it is running. Tk
+ also doesn't consistently track changes in the system colors.
+
+If you have comments or bug reports for the Windows version of Tk,
+please direct them to:
+
+Scott Stanton
+scott.stanton@eng.sun.com
+
+or post them to the newsgroup comp.lang.tcl.
diff --git a/win/makefile.bc b/win/makefile.bc
new file mode 100644
index 0000000..a77c0ed
--- /dev/null
+++ b/win/makefile.bc
@@ -0,0 +1,341 @@
+# Borland C++ 4.5 makefile for Tk
+#
+# Copyright (c) 1995-1996 by Sun Microsystems, Inc.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# SCCS: @(#) makefile.bc 1.73 97/11/05 16:12:27
+
+
+#
+# Project directories
+#
+# ROOT = top of source tree
+# TMPDIR = location where .obj files should be stored during build
+# TCLDIR = location of top of Tcl source heirarchy
+#
+
+ROOT = ..
+TMPDIR = .
+TOOLS = c:\bc45
+TCLDIR = ..\..\tcl8.0
+
+# uncomment the following line to compile with symbols
+#DEBUG=1
+
+# uncomment the following line to compile with TCL_MEM_DEBUG
+#DEBUGDEFINES =TCL_MEM_DEBUG
+
+#
+# Borland C++ tools
+#
+
+BORLAND = $(TOOLS)
+IMPLIB = $(BORLAND)\bin\Implib
+BCC32 = $(BORLAND)\bin\Bcc32
+TLINK32 = $(BORLAND)\bin\tlink32
+RC = $(BORLAND)\bin\brcc32
+CP = copy
+RM = del
+
+INCLUDES = $(BORLAND)\include;$(ROOT)\generic;$(ROOT)\bitmaps;$(ROOT)\xlib;$(ROOT)\win;$(TCLDIR)\generic
+LIBDIRS = $(BORLAND)\lib;$(ROOT)\win
+TCLLIBDIR = $(TCLDIR)\win
+
+
+!ifndef DEBUG
+
+# these macros cause maximum optimization and no symbols
+DEBUGLDFLAGS =
+DEBUGCCFLAGS = -v- -vi- -O2
+
+!else
+
+# these macros enable debugging
+DEBUGLDFLAGS = -v
+DEBUGCCFLAGS = -k -Od -v
+
+!endif
+
+DEFINES = MT;_RTLDLL;STRICT;$(DEBUGDEFINES)
+PROJECTCCFLAGS= $(DEBUGCCFLAGS) -w-par -w-stu
+
+LNFLAGS_exe = -Tpe -aa -c $(DEBUGLDFLAGS) $(BORLAND)\lib\c0w32
+LNFLAGS_dll = -Tpd -aa -c $(DEBUGLDFLAGS) $(BORLAND)\lib\c0d32
+
+LNLIBS_exe = $(TKLIB) $(TCLLIBDIR)\$(TCLLIB) import32 cw32mti
+LNLIBS_dll = $(TCLLIBDIR)\$(TCLLIB) import32 cw32mti
+
+#
+# Global makefile settings
+#
+
+.AUTODEPEND
+.CACHEAUTODEPEND
+
+.suffixes: .c .dll .lib .obj .exe
+
+.path.c=$(ROOT)\win;$(ROOT)\generic;$(ROOT)\xlib;$(ROOT)\unix
+.path.obj=$(TMPDIR)
+
+WISHOBJS = \
+ $(TMPDIR)\tkConsole.obj \
+ $(TMPDIR)\winMain.obj
+
+TKTESTOBJS = \
+ $(TMPDIR)\tkConsole.obj \
+ $(TMPDIR)\tkTest.obj \
+ $(TMPDIR)\tkSquare.obj \
+ $(TMPDIR)\testMain.obj
+
+XLIBOBJS = \
+ $(TMPDIR)\xcolors.obj \
+ $(TMPDIR)\xdraw.obj \
+ $(TMPDIR)\xgc.obj \
+ $(TMPDIR)\ximage.obj \
+ $(TMPDIR)\xutil.obj
+
+TKOBJS = \
+ $(TMPDIR)\tkUnixMenubu.obj \
+ $(TMPDIR)\tkUnixScale.obj \
+ $(XLIBOBJS) \
+ $(TMPDIR)\tkWin3d.obj \
+ $(TMPDIR)\tkWin32Dll.obj \
+ $(TMPDIR)\tkWinButton.obj \
+ $(TMPDIR)\tkWinClipboard.obj \
+ $(TMPDIR)\tkWinColor.obj \
+ $(TMPDIR)\tkWinCursor.obj \
+ $(TMPDIR)\tkWinDialog.obj \
+ $(TMPDIR)\tkWinDraw.obj \
+ $(TMPDIR)\tkWinEmbed.obj \
+ $(TMPDIR)\tkWinFont.obj \
+ $(TMPDIR)\tkWinImage.obj \
+ $(TMPDIR)\tkWinInit.obj \
+ $(TMPDIR)\tkWinKey.obj \
+ $(TMPDIR)\tkWinMenu.obj \
+ $(TMPDIR)\tkWinPixmap.obj \
+ $(TMPDIR)\tkWinPointer.obj \
+ $(TMPDIR)\tkWinRegion.obj \
+ $(TMPDIR)\tkWinScrlbr.obj \
+ $(TMPDIR)\tkWinSend.obj \
+ $(TMPDIR)\tkWinWindow.obj \
+ $(TMPDIR)\tkWinWm.obj \
+ $(TMPDIR)\tkWinX.obj \
+ $(TMPDIR)\stubs.obj \
+ $(TMPDIR)\tk3d.obj \
+ $(TMPDIR)\tkArgv.obj \
+ $(TMPDIR)\tkAtom.obj \
+ $(TMPDIR)\tkBind.obj \
+ $(TMPDIR)\tkBitmap.obj \
+ $(TMPDIR)\tkButton.obj \
+ $(TMPDIR)\tkCanvArc.obj \
+ $(TMPDIR)\tkCanvBmap.obj \
+ $(TMPDIR)\tkCanvImg.obj \
+ $(TMPDIR)\tkCanvLine.obj \
+ $(TMPDIR)\tkCanvPoly.obj \
+ $(TMPDIR)\tkCanvPs.obj \
+ $(TMPDIR)\tkCanvText.obj \
+ $(TMPDIR)\tkCanvUtil.obj \
+ $(TMPDIR)\tkCanvWind.obj \
+ $(TMPDIR)\tkCanvas.obj \
+ $(TMPDIR)\tkClipboard.obj \
+ $(TMPDIR)\tkCmds.obj \
+ $(TMPDIR)\tkColor.obj \
+ $(TMPDIR)\tkConfig.obj \
+ $(TMPDIR)\tkCursor.obj \
+ $(TMPDIR)\tkEntry.obj \
+ $(TMPDIR)\tkError.obj \
+ $(TMPDIR)\tkEvent.obj \
+ $(TMPDIR)\tkFileFilter.obj \
+ $(TMPDIR)\tkFocus.obj \
+ $(TMPDIR)\tkFont.obj \
+ $(TMPDIR)\tkFrame.obj \
+ $(TMPDIR)\tkGC.obj \
+ $(TMPDIR)\tkGeometry.obj \
+ $(TMPDIR)\tkGet.obj \
+ $(TMPDIR)\tkGrab.obj \
+ $(TMPDIR)\tkGrid.obj \
+ $(TMPDIR)\tkImage.obj \
+ $(TMPDIR)\tkImgBmap.obj \
+ $(TMPDIR)\tkImgGIF.obj \
+ $(TMPDIR)\tkImgPPM.obj \
+ $(TMPDIR)\tkImgPhoto.obj \
+ $(TMPDIR)\tkImgUtil.obj \
+ $(TMPDIR)\tkListbox.obj \
+ $(TMPDIR)\tkMacWinMenu.obj \
+ $(TMPDIR)\tkMain.obj \
+ $(TMPDIR)\tkMenu.obj \
+ $(TMPDIR)\tkMenubutton.obj \
+ $(TMPDIR)\tkMenuDraw.obj \
+ $(TMPDIR)\tkMessage.obj \
+ $(TMPDIR)\tkOption.obj \
+ $(TMPDIR)\tkPack.obj \
+ $(TMPDIR)\tkPlace.obj \
+ $(TMPDIR)\tkPointer.obj \
+ $(TMPDIR)\tkRectOval.obj \
+ $(TMPDIR)\tkScale.obj \
+ $(TMPDIR)\tkScrollbar.obj \
+ $(TMPDIR)\tkSelect.obj \
+ $(TMPDIR)\tkText.obj \
+ $(TMPDIR)\tkTextBTree.obj \
+ $(TMPDIR)\tkTextDisp.obj \
+ $(TMPDIR)\tkTextImage.obj \
+ $(TMPDIR)\tkTextIndex.obj \
+ $(TMPDIR)\tkTextMark.obj \
+ $(TMPDIR)\tkTextTag.obj \
+ $(TMPDIR)\tkTextWind.obj \
+ $(TMPDIR)\tkTrig.obj \
+ $(TMPDIR)\tkUtil.obj \
+ $(TMPDIR)\tkVisual.obj \
+ $(TMPDIR)\tkWindow.obj
+
+TCLDLL = tcl80.dll
+TCLLIB = tcl80.lib
+TKDLL = tk80.dll
+TKLIB = tk80.lib
+WISH = wish80.exe
+TKTEST = tktest.exe
+
+#
+# Targets
+#
+
+all: cfgdll $(TKDLL) cfgexe $(WISH) cfgcln
+tktest: cfgdll $(TKDLL) cfgtest $(TKTEST) cfgcln
+
+test: tktest
+ $(TKTEST) &&|
+ cd ../tests
+ console show
+ update
+ source all
+|
+
+# Implicit Targets
+
+.c.obj:
+ @$(BCC32) {$< }
+
+.dll.lib:
+ $(IMPLIB) -c $@ $<
+
+.rc.res:
+ $(RC) -i$(INCLUDES) $<
+
+#
+# Special case object file targets
+#
+
+$(TMPDIR)\testMain.obj : $(ROOT)\win\winMain.c
+ $(BCC32) -c -o$@ $(ROOT)\win\winMain.c
+
+#
+# Configuration file targets - these files are implicitly used by the compiler
+#
+
+cfgdll:
+ @$(CP) &&|
+ -n$(TMPDIR) -I$(INCLUDES) -c -WM
+ -D$(DEFINES) -3 -d $(PROJECTCCFLAGS)
+| bcc32.cfg >NUL
+
+cfgexe:
+ @$(CP) &&|
+ -n$(TMPDIR) -I$(INCLUDES) -c -W
+ -D$(DEFINES) -3 -d $(PROJECTCCFLAGS)
+| bcc32.cfg >NUL
+
+cfgtest:
+ @$(CP) &&|
+ -n$(TMPDIR) -I$(INCLUDES) -c -W
+ -D$(DEFINES);TK_TEST -3 -d $(PROJECTCCFLAGS)
+| bcc32.cfg >NUL
+
+cfgcln:
+ @$(RM) bcc32.cfg
+
+#
+# Executable targets
+#
+
+$(TKDLL): $(TKOBJS) tk.def rc\tk.res
+ $(TLINK32) @&&|
+$(LNFLAGS_dll) $(TKOBJS)
+$@
+-x
+$(LNLIBS_dll)
+tk.def
+rc\tk.res
+|
+
+$(WISH): $(WISHOBJS) $(TKLIB) rc\wish.res
+ $(TLINK32) @&&|
+$(LNFLAGS_exe) $(WISHOBJS)
+$@
+-x
+$(LNLIBS_exe)
+|, &&|
+EXETYPE WINDOWS
+CODE PRELOAD MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE MULTIPLE
+|, rc\wish.res
+
+$(TKTEST): $(TKTESTOBJS) $(TKLIB)
+ $(TLINK32) $(LNFLAGS_exe) @&&|
+$(TKTESTOBJS)
+$@
+-x
+$(LNLIBS_exe)
+|, &&|
+EXETYPE WINDOWS
+CODE PRELOAD MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE MULTIPLE
+|,
+
+#
+# Other dependencies
+#
+
+rc\wish.res: rc\wish.ico
+rc\tk.res: rc\tk.ico rc\*.cur
+
+# The following rule automatically generates a tk.def file containing
+# an export entry for every public symbol in the $(TKDLL) library.
+
+tk.def: $(TKOBJS)
+ $(TCLLIBDIR)\dumpexts.exe -o $@ $(TKDLL) @&&|
+ $(TKOBJS)
+|
+
+# rule to build library object files
+
+# debugging rules, the .dll and .exe files must be in the same
+# directory as the object files for debugging purposes
+
+$(TMPDIR)\$(TKDLL): $(TKDLL)
+ $(CP) $(TKDLL) $(TMPDIR)
+
+$(TMPDIR)\$(TCLDLL): $(TCLLIBDIR)\$(TCLDLL)
+ $(CP) $(TCLLIBDIR)\$(TCLDLL) $(TMPDIR)
+
+$(TMPDIR)\$(WISH): $(WISH)
+ $(CP) $(WISH) $(TMPDIR)
+
+$(TMPDIR)\$(TKTEST): $(TKTEST)
+ $(CP) $(TKTEST) $(TMPDIR)
+
+debug: $(TMPDIR)\$(TKDLL) $(TMPDIR)\$(TCLDLL) $(TMPDIR)\$(TKTEST)
+
+
+# remove all generated files
+
+clean:
+ $(RM) $(WISH)
+ $(RM) $(TKTEST)
+ $(RM) $(TKLIB)
+ $(RM) $(TKDLL)
+ $(RM) rc\*.res
+ $(RM) tk.def
+ $(RM) $(TMPDIR)\*.obj
+ $(RM) *.cfg
diff --git a/win/makefile.vc b/win/makefile.vc
new file mode 100644
index 0000000..7312db0
--- /dev/null
+++ b/win/makefile.vc
@@ -0,0 +1,397 @@
+# Visual C++ 2.x and 4.0 makefile
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# Copyright (c) 1995-1996 Sun Microsystems, Inc.
+# SCCS: @(#) makefile.vc 1.64 97/10/27 17:27:20
+
+# Does not depend on the presence of any environment variables in
+# order to compile tcl; all needed information is derived from
+# location of the compiler directories.
+
+#
+# Project directories
+#
+# ROOT = top of source tree
+#
+# TMPDIR = location where .obj files should be stored during build
+#
+# TOOLS32 = location of VC++ 32-bit development tools. Note that the
+# VC++ 2.0 header files are broken, so you need to use the
+# ones that come with the developer network CD's, or later
+# versions of VC++.
+#
+# TCLDIR = location of top of Tcl source heirarchy
+#
+
+ROOT = ..
+TMPDIR = .
+TOOLS32 = c:\msdev
+TCLDIR = ..\..\tcl8.0
+
+# Set this to the appropriate value of /MACHINE: for your platform
+MACHINE = IX86
+
+# Comment the following line to compile with symbols
+NODEBUG=1
+
+# uncomment the following two lines to compile with TCL_MEM_DEBUG
+#DEBUGDEFINES =-DTCL_MEM_DEBUG
+
+######################################################################
+# Do not modify below this line
+######################################################################
+
+VERSION = 80
+
+TCLDLL = tcl$(VERSION).dll
+TCLLIB = tcl$(VERSION).lib
+TCLPLUGINDLL = tcl$(VERSION)p.dll
+TCLPLUGINLIB = tcl$(VERSION)p.lib
+TKDLL = tk$(VERSION).dll
+TKLIB = tk$(VERSION).lib
+TKPLUGINDLL = tk$(VERSION)p.dll
+TKPLUGINLIB = tk$(VERSION)p.lib
+
+WISH = wish$(VERSION).exe
+WISHP = wishp$(VERSION).exe
+TKTEST = tktest.exe
+DUMPEXTS = $(TMPDIR)\dumpexts.exe
+
+WISHOBJS = \
+ $(TMPDIR)\tkConsole.obj \
+ $(TMPDIR)\winMain.obj
+
+TKTESTOBJS = \
+ $(TMPDIR)\tkConsole.obj \
+ $(TMPDIR)\tkTest.obj \
+ $(TMPDIR)\tkSquare.obj \
+ $(TMPDIR)\testMain.obj
+
+XLIBOBJS = \
+ $(TMPDIR)\xcolors.obj \
+ $(TMPDIR)\xdraw.obj \
+ $(TMPDIR)\xgc.obj \
+ $(TMPDIR)\ximage.obj \
+ $(TMPDIR)\xutil.obj
+
+TKOBJS = \
+ $(TMPDIR)\tkUnixMenubu.obj \
+ $(TMPDIR)\tkUnixScale.obj \
+ $(XLIBOBJS) \
+ $(TMPDIR)\tkWin3d.obj \
+ $(TMPDIR)\tkWin32Dll.obj \
+ $(TMPDIR)\tkWinButton.obj \
+ $(TMPDIR)\tkWinClipboard.obj \
+ $(TMPDIR)\tkWinColor.obj \
+ $(TMPDIR)\tkWinCursor.obj \
+ $(TMPDIR)\tkWinDialog.obj \
+ $(TMPDIR)\tkWinDraw.obj \
+ $(TMPDIR)\tkWinEmbed.obj \
+ $(TMPDIR)\tkWinFont.obj \
+ $(TMPDIR)\tkWinImage.obj \
+ $(TMPDIR)\tkWinInit.obj \
+ $(TMPDIR)\tkWinKey.obj \
+ $(TMPDIR)\tkWinMenu.obj \
+ $(TMPDIR)\tkWinPixmap.obj \
+ $(TMPDIR)\tkWinPointer.obj \
+ $(TMPDIR)\tkWinRegion.obj \
+ $(TMPDIR)\tkWinScrlbr.obj \
+ $(TMPDIR)\tkWinSend.obj \
+ $(TMPDIR)\tkWinWindow.obj \
+ $(TMPDIR)\tkWinWm.obj \
+ $(TMPDIR)\tkWinX.obj \
+ $(TMPDIR)\stubs.obj \
+ $(TMPDIR)\tk3d.obj \
+ $(TMPDIR)\tkArgv.obj \
+ $(TMPDIR)\tkAtom.obj \
+ $(TMPDIR)\tkBind.obj \
+ $(TMPDIR)\tkBitmap.obj \
+ $(TMPDIR)\tkButton.obj \
+ $(TMPDIR)\tkCanvArc.obj \
+ $(TMPDIR)\tkCanvBmap.obj \
+ $(TMPDIR)\tkCanvImg.obj \
+ $(TMPDIR)\tkCanvLine.obj \
+ $(TMPDIR)\tkCanvPoly.obj \
+ $(TMPDIR)\tkCanvPs.obj \
+ $(TMPDIR)\tkCanvText.obj \
+ $(TMPDIR)\tkCanvUtil.obj \
+ $(TMPDIR)\tkCanvWind.obj \
+ $(TMPDIR)\tkCanvas.obj \
+ $(TMPDIR)\tkClipboard.obj \
+ $(TMPDIR)\tkCmds.obj \
+ $(TMPDIR)\tkColor.obj \
+ $(TMPDIR)\tkConfig.obj \
+ $(TMPDIR)\tkCursor.obj \
+ $(TMPDIR)\tkEntry.obj \
+ $(TMPDIR)\tkError.obj \
+ $(TMPDIR)\tkEvent.obj \
+ $(TMPDIR)\tkFileFilter.obj \
+ $(TMPDIR)\tkFocus.obj \
+ $(TMPDIR)\tkFont.obj \
+ $(TMPDIR)\tkFrame.obj \
+ $(TMPDIR)\tkGC.obj \
+ $(TMPDIR)\tkGeometry.obj \
+ $(TMPDIR)\tkGet.obj \
+ $(TMPDIR)\tkGrab.obj \
+ $(TMPDIR)\tkGrid.obj \
+ $(TMPDIR)\tkImage.obj \
+ $(TMPDIR)\tkImgBmap.obj \
+ $(TMPDIR)\tkImgGIF.obj \
+ $(TMPDIR)\tkImgPPM.obj \
+ $(TMPDIR)\tkImgPhoto.obj \
+ $(TMPDIR)\tkImgUtil.obj \
+ $(TMPDIR)\tkListbox.obj \
+ $(TMPDIR)\tkMacWinMenu.obj \
+ $(TMPDIR)\tkMain.obj \
+ $(TMPDIR)\tkMenu.obj \
+ $(TMPDIR)\tkMenubutton.obj \
+ $(TMPDIR)\tkMenuDraw.obj \
+ $(TMPDIR)\tkMessage.obj \
+ $(TMPDIR)\tkOption.obj \
+ $(TMPDIR)\tkPack.obj \
+ $(TMPDIR)\tkPlace.obj \
+ $(TMPDIR)\tkPointer.obj \
+ $(TMPDIR)\tkRectOval.obj \
+ $(TMPDIR)\tkScale.obj \
+ $(TMPDIR)\tkScrollbar.obj \
+ $(TMPDIR)\tkSelect.obj \
+ $(TMPDIR)\tkText.obj \
+ $(TMPDIR)\tkTextBTree.obj \
+ $(TMPDIR)\tkTextDisp.obj \
+ $(TMPDIR)\tkTextImage.obj \
+ $(TMPDIR)\tkTextIndex.obj \
+ $(TMPDIR)\tkTextMark.obj \
+ $(TMPDIR)\tkTextTag.obj \
+ $(TMPDIR)\tkTextWind.obj \
+ $(TMPDIR)\tkTrig.obj \
+ $(TMPDIR)\tkUtil.obj \
+ $(TMPDIR)\tkVisual.obj \
+ $(TMPDIR)\tkWindow.obj
+
+cc32 = $(TOOLS32)\bin\cl.exe
+link32 = $(TOOLS32)\bin\link.exe
+rc32 = $(TOOLS32)\bin\rc.exe
+include32 = -I$(TOOLS32)\include
+
+WINDIR = $(ROOT)\win
+GENERICDIR = $(ROOT)\generic
+XLIBDIR = $(ROOT)\xlib
+BITMAPDIR = $(ROOT)\bitmaps
+TCLLIBDIR = $(TCLDIR)\win
+RCDIR = $(WINDIR)\rc
+
+TK_INCLUDES = -I$(WINDIR) -I$(GENERICDIR) -I$(BITMAPDIR) -I$(XLIBDIR) \
+ -I$(TCLDIR)\generic
+TK_DEFINES = $(DEBUGDEFINES)
+
+TK_CFLAGS = $(cdebug) $(cflags) $(cvarsdll) $(include32) \
+ $(TK_INCLUDES) $(TK_DEFINES)
+
+######################################################################
+# Link flags
+######################################################################
+
+!IFDEF NODEBUG
+ldebug = /RELEASE
+!ELSE
+ldebug = -debug:full -debugtype:cv
+!ENDIF
+
+# declarations common to all linker options
+lcommon = /NODEFAULTLIB /RELEASE /NOLOGO
+
+# declarations for use on Intel i386, i486, and Pentium systems
+!IF "$(MACHINE)" == "IX86"
+DLLENTRY = @12
+lflags = $(lcommon) -align:0x1000 /MACHINE:$(MACHINE)
+!ELSE
+lflags = $(lcommon) /MACHINE:$(MACHINE)
+!ENDIF
+
+conlflags = $(lflags) -subsystem:console -entry:mainCRTStartup
+guilflags = $(lflags) -subsystem:windows -entry:WinMainCRTStartup
+dlllflags = $(lflags) -entry:_DllMainCRTStartup$(DLLENTRY) -dll
+
+!IF "$(MACHINE)" == "PPC"
+libc = libc.lib
+libcdll = crtdll.lib
+!ELSE
+libc = libc.lib oldnames.lib
+libcdll = msvcrt.lib oldnames.lib
+!ENDIF
+
+baselibs = kernel32.lib $(optlibs) advapi32.lib
+winlibs = $(baselibs) user32.lib gdi32.lib comdlg32.lib winspool.lib
+guilibs = $(libc) $(winlibs)
+
+guilibsdll = $(libcdll) $(winlibs)
+
+######################################################################
+# Compile flags
+######################################################################
+
+!IFDEF NODEBUG
+cdebug = -Oti -Gs -GD
+!ELSE
+cdebug = -Z7 -Od -WX
+!ENDIF
+
+# declarations common to all compiler options
+ccommon = -c -W3 -nologo -YX
+
+!IF "$(MACHINE)" == "IX86"
+cflags = $(ccommon) -D_X86_=1
+!ELSE
+!IF "$(MACHINE)" == "MIPS"
+cflags = $(ccommon) -D_MIPS_=1
+!ELSE
+!IF "$(MACHINE)" == "PPC"
+cflags = $(ccommon) -D_PPC_=1
+!ELSE
+!IF "$(MACHINE)" == "ALPHA"
+cflags = $(ccommon) -D_ALPHA_=1
+!ENDIF
+!ENDIF
+!ENDIF
+!ENDIF
+
+cvars = -DWIN32 -D_WIN32
+cvarsmt = $(cvars) -D_MT
+cvarsdll = $(cvarsmt) -D_DLL
+
+CON_CFLAGS = $(cdebug) $(cflags) $(cvars) $(include32) -DCONSOLE
+
+######################################################################
+# Project specific targets
+######################################################################
+
+all: $(WISH)
+test: $(TKTEST)
+plugin: $(TKPLUGINDLL) $(WISHP)
+
+$(TKLIB): $(TKDLL)
+
+$(TKDLL): $(TKOBJS) $(TMPDIR)\tk.res $(TMPDIR)\tk.def
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(dlllflags) -def:$(TMPDIR)\tk.def \
+ -out:$@ $(TMPDIR)\tk.res $(TCLLIBDIR)\$(TCLLIB) \
+ $(guilibsdll) @<<
+ $(TKOBJS)
+<<
+
+$(TKPLUGINLIB): $(TKPLUGINDLL)
+
+$(TKPLUGINDLL): $(TKOBJS) $(TMPDIR)\tk.res $(TMPDIR)\plugin.def
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(dlllflags) -def:$(TMPDIR)\plugin.def \
+ -out:$@ $(TMPDIR)\tk.res $(TCLLIBDIR)\$(TCLPLUGINLIB) \
+ $(guilibsdll) @<<
+ $(TKOBJS)
+<<
+
+$(WISH): $(WISHOBJS) $(TKLIB) $(TMPDIR)\wish.res
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(guilflags) $(TMPDIR)\wish.res -out:$@ \
+ $(guilibsdll) $(TCLLIBDIR)\$(TCLLIB) $(TKLIB) $(WISHOBJS)
+
+$(WISHP): $(WISHOBJS) $(TKPLUGINLIB) $(TMPDIR)\wish.res
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(guilflags) $(TMPDIR)\wish.res -out:$@ \
+ $(guilibsdll) $(TCLLIBDIR)\$(TCLPLUGINLIB) \
+ $(TKPLUGINLIB) $(WISHOBJS)
+
+$(TKTEST): $(TKTESTOBJS) $(TKLIB) $(TMPDIR)\wish.res
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(guilflags) $(TMPDIR)\wish.res -out:$@ \
+ $(guilibsdll) $(TCLLIBDIR)\$(TCLLIB) $(TKLIB) $(TKTESTOBJS)
+
+$(TMPDIR)\tk.def: $(DUMPEXTS) $(TKOBJS)
+ $(DUMPEXTS) -o $@ $(TKDLL) @<<
+ $(TKOBJS)
+<<
+
+$(TMPDIR)\plugin.def: $(DUMPEXTS) $(TKOBJS)
+ $(DUMPEXTS) -o $@ $(TKPLUGINDLL) @<<
+ $(TKOBJS)
+<<
+
+$(DUMPEXTS): $(TCLDIR)\win\winDumpExts.c
+ $(cc32) $(CON_CFLAGS) -Fo$(TMPDIR)\ $?
+ set LIB=$(TOOLS32)\lib
+ $(link32) $(ldebug) $(conlflags) $(guilibs) -out:$@ \
+ $(TMPDIR)\winDumpExts.obj
+
+#
+# Special case object file targets
+#
+
+$(TMPDIR)\testMain.obj: $(ROOT)\win\winMain.c
+ $(cc32) $(TK_CFLAGS) -DTK_TEST -Fo$@ $?
+
+#
+# Implicit rules
+#
+
+{$(XLIBDIR)}.c{$(TMPDIR)}.obj:
+ $(cc32) $(TK_CFLAGS) -Fo$(TMPDIR)\ $<
+
+{$(GENERICDIR)}.c{$(TMPDIR)}.obj:
+ $(cc32) $(TK_CFLAGS) -Fo$(TMPDIR)\ $<
+
+{$(WINDIR)}.c{$(TMPDIR)}.obj:
+ $(cc32) $(TK_CFLAGS) -Fo$(TMPDIR)\ $<
+
+{$(ROOT)\unix}.c{$(TMPDIR)}.obj:
+ $(cc32) $(TK_CFLAGS) -Fo$(TMPDIR)\ $<
+
+{$(RCDIR)}.rc{$(TMPDIR)}.res:
+ $(rc32) -fo $@ -r -i $(GENERICDIR) $<
+
+clean:
+ -@del *.exp
+ -@del *.lib
+ -@del *.dll
+ -@del *.exe
+ -@del $(TMPDIR)\*.obj
+ -@del $(TMPDIR)\*.res
+ -@del $(TMPDIR)\*.def
+
+# dependencies
+
+$(TMPDIR)\tk.res: \
+ $(RCDIR)\buttons.bmp \
+ $(RCDIR)\cursor*.cur \
+ $(RCDIR)\tk.ico
+
+$(GENERICDIR)/default.h: $(WINDIR)/tkWinDefault.h
+$(GENERICDIR)/tkButton.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkCanvas.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkEntry.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkFrame.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkListbox.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkMenu.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkMenubutton.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkMessage.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkScale.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkScrollbar.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkText.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkTextIndex.c: $(GENERICDIR)/default.h
+$(GENERICDIR)/tkTextTag.c: $(GENERICDIR)/default.h
+
+$(GENERICDIR)/tkText.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextBTree.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextDisp.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextDisp.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextImage.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextIndex.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextMark.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextTag.c: $(GENERICDIR)/tkText.h
+$(GENERICDIR)/tkTextWind.c: $(GENERICDIR)/tkText.h
+
+$(GENERICDIR)/tkMacWinMenu.c: $(GENERICDIR)/tkMenu.h
+$(GENERICDIR)/tkMenu.c: $(GENERICDIR)/tkMenu.h
+$(GENERICDIR)/tkMenuDraw.c: $(GENERICDIR)/tkMenu.h
+$(WINDIR)/tkWinMenu.c: $(GENERICDIR)/tkMenu.h
+
diff --git a/win/rc/buttons.bmp b/win/rc/buttons.bmp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/buttons.bmp
diff --git a/win/rc/cursor00.cur b/win/rc/cursor00.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor00.cur
diff --git a/win/rc/cursor02.cur b/win/rc/cursor02.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor02.cur
diff --git a/win/rc/cursor04.cur b/win/rc/cursor04.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor04.cur
diff --git a/win/rc/cursor06.cur b/win/rc/cursor06.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor06.cur
diff --git a/win/rc/cursor08.cur b/win/rc/cursor08.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor08.cur
diff --git a/win/rc/cursor0a.cur b/win/rc/cursor0a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor0a.cur
diff --git a/win/rc/cursor0c.cur b/win/rc/cursor0c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor0c.cur
diff --git a/win/rc/cursor0e.cur b/win/rc/cursor0e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor0e.cur
diff --git a/win/rc/cursor10.cur b/win/rc/cursor10.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor10.cur
diff --git a/win/rc/cursor12.cur b/win/rc/cursor12.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor12.cur
diff --git a/win/rc/cursor14.cur b/win/rc/cursor14.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor14.cur
diff --git a/win/rc/cursor16.cur b/win/rc/cursor16.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor16.cur
diff --git a/win/rc/cursor18.cur b/win/rc/cursor18.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor18.cur
diff --git a/win/rc/cursor1a.cur b/win/rc/cursor1a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor1a.cur
diff --git a/win/rc/cursor1c.cur b/win/rc/cursor1c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor1c.cur
diff --git a/win/rc/cursor1e.cur b/win/rc/cursor1e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor1e.cur
diff --git a/win/rc/cursor20.cur b/win/rc/cursor20.cur
new file mode 100644
index 0000000..95c4dc1
--- /dev/null
+++ b/win/rc/cursor20.cur
@@ -0,0 +1,2 @@
+ 0( @ÿÿÿB„¢ŠR”*¨Ð
+ ü~ü~
diff --git a/win/rc/cursor22.cur b/win/rc/cursor22.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor22.cur
diff --git a/win/rc/cursor24.cur b/win/rc/cursor24.cur
new file mode 100644
index 0000000..c971b66
--- /dev/null
+++ b/win/rc/cursor24.cur
@@ -0,0 +1,2 @@
+ 0( @ÿÿÿ€À
+ "ˆB„þþþþB„"ˆ
diff --git a/win/rc/cursor26.cur b/win/rc/cursor26.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor26.cur
diff --git a/win/rc/cursor28.cur b/win/rc/cursor28.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor28.cur
diff --git a/win/rc/cursor2a.cur b/win/rc/cursor2a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor2a.cur
diff --git a/win/rc/cursor2c.cur b/win/rc/cursor2c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor2c.cur
diff --git a/win/rc/cursor2e.cur b/win/rc/cursor2e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor2e.cur
diff --git a/win/rc/cursor30.cur b/win/rc/cursor30.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor30.cur
diff --git a/win/rc/cursor32.cur b/win/rc/cursor32.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor32.cur
diff --git a/win/rc/cursor34.cur b/win/rc/cursor34.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor34.cur
diff --git a/win/rc/cursor36.cur b/win/rc/cursor36.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor36.cur
diff --git a/win/rc/cursor38.cur b/win/rc/cursor38.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor38.cur
diff --git a/win/rc/cursor3a.cur b/win/rc/cursor3a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor3a.cur
diff --git a/win/rc/cursor3c.cur b/win/rc/cursor3c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor3c.cur
diff --git a/win/rc/cursor3e.cur b/win/rc/cursor3e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor3e.cur
diff --git a/win/rc/cursor40.cur b/win/rc/cursor40.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor40.cur
diff --git a/win/rc/cursor42.cur b/win/rc/cursor42.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor42.cur
diff --git a/win/rc/cursor44.cur b/win/rc/cursor44.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor44.cur
diff --git a/win/rc/cursor46.cur b/win/rc/cursor46.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor46.cur
diff --git a/win/rc/cursor48.cur b/win/rc/cursor48.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor48.cur
diff --git a/win/rc/cursor4a.cur b/win/rc/cursor4a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor4a.cur
diff --git a/win/rc/cursor4c.cur b/win/rc/cursor4c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor4c.cur
diff --git a/win/rc/cursor4e.cur b/win/rc/cursor4e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor4e.cur
diff --git a/win/rc/cursor50.cur b/win/rc/cursor50.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor50.cur
diff --git a/win/rc/cursor52.cur b/win/rc/cursor52.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor52.cur
diff --git a/win/rc/cursor54.cur b/win/rc/cursor54.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor54.cur
diff --git a/win/rc/cursor56.cur b/win/rc/cursor56.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor56.cur
diff --git a/win/rc/cursor58.cur b/win/rc/cursor58.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor58.cur
diff --git a/win/rc/cursor5a.cur b/win/rc/cursor5a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor5a.cur
diff --git a/win/rc/cursor5c.cur b/win/rc/cursor5c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor5c.cur
diff --git a/win/rc/cursor5e.cur b/win/rc/cursor5e.cur
new file mode 100644
index 0000000..9141887
--- /dev/null
+++ b/win/rc/cursor5e.cur
@@ -0,0 +1 @@
+
diff --git a/win/rc/cursor60.cur b/win/rc/cursor60.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor60.cur
diff --git a/win/rc/cursor62.cur b/win/rc/cursor62.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor62.cur
diff --git a/win/rc/cursor64.cur b/win/rc/cursor64.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor64.cur
diff --git a/win/rc/cursor66.cur b/win/rc/cursor66.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor66.cur
diff --git a/win/rc/cursor68.cur b/win/rc/cursor68.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor68.cur
diff --git a/win/rc/cursor6a.cur b/win/rc/cursor6a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor6a.cur
diff --git a/win/rc/cursor6c.cur b/win/rc/cursor6c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor6c.cur
diff --git a/win/rc/cursor6e.cur b/win/rc/cursor6e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor6e.cur
diff --git a/win/rc/cursor70.cur b/win/rc/cursor70.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor70.cur
diff --git a/win/rc/cursor72.cur b/win/rc/cursor72.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor72.cur
diff --git a/win/rc/cursor74.cur b/win/rc/cursor74.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor74.cur
diff --git a/win/rc/cursor76.cur b/win/rc/cursor76.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor76.cur
diff --git a/win/rc/cursor78.cur b/win/rc/cursor78.cur
new file mode 100644
index 0000000..53ab3d0
--- /dev/null
+++ b/win/rc/cursor78.cur
@@ -0,0 +1 @@
+ 0( @ÿÿÿþ
diff --git a/win/rc/cursor7a.cur b/win/rc/cursor7a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor7a.cur
diff --git a/win/rc/cursor7c.cur b/win/rc/cursor7c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor7c.cur
diff --git a/win/rc/cursor7e.cur b/win/rc/cursor7e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor7e.cur
diff --git a/win/rc/cursor80.cur b/win/rc/cursor80.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor80.cur
diff --git a/win/rc/cursor82.cur b/win/rc/cursor82.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor82.cur
diff --git a/win/rc/cursor84.cur b/win/rc/cursor84.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor84.cur
diff --git a/win/rc/cursor86.cur b/win/rc/cursor86.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor86.cur
diff --git a/win/rc/cursor88.cur b/win/rc/cursor88.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor88.cur
diff --git a/win/rc/cursor8a.cur b/win/rc/cursor8a.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor8a.cur
diff --git a/win/rc/cursor8c.cur b/win/rc/cursor8c.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor8c.cur
diff --git a/win/rc/cursor8e.cur b/win/rc/cursor8e.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor8e.cur
diff --git a/win/rc/cursor90.cur b/win/rc/cursor90.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor90.cur
diff --git a/win/rc/cursor92.cur b/win/rc/cursor92.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor92.cur
diff --git a/win/rc/cursor94.cur b/win/rc/cursor94.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor94.cur
diff --git a/win/rc/cursor96.cur b/win/rc/cursor96.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor96.cur
diff --git a/win/rc/cursor98.cur b/win/rc/cursor98.cur
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/cursor98.cur
diff --git a/win/rc/tk.ico b/win/rc/tk.ico
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/tk.ico
diff --git a/win/rc/tk.rc b/win/rc/tk.rc
new file mode 100644
index 0000000..0d74ec3
--- /dev/null
+++ b/win/rc/tk.rc
@@ -0,0 +1,132 @@
+// SCCS: @(#) tk.rc 1.22 97/03/21 18:35:14
+//
+// Version
+//
+
+#define RESOURCE_INCLUDED
+#include <tk.h>
+
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+ PRODUCTVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0x0L
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Tk DLL\0"
+ VALUE "OriginalFilename", "tk" STRINGIFY(TK_MAJOR_VERSION) STRINGIFY(TK_MINOR_VERSION) ".dll\0"
+ VALUE "CompanyName", "Sun Microsystems, Inc.\0"
+ VALUE "FileVersion", TK_PATCH_LEVEL
+ VALUE "LegalCopyright", "Copyright \251 1995-1996\0"
+ VALUE "ProductName", "Tk " TK_VERSION " for Windows\0"
+ VALUE "ProductVersion", TK_PATCH_LEVEL
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+//
+// Icons
+//
+
+tk ICON DISCARDABLE "tk.ico"
+
+//
+// Bitmaps
+//
+
+buttons BITMAP DISCARDABLE "buttons.bmp"
+
+//
+// Cursors
+//
+
+X_cursor CURSOR DISCARDABLE "cursor00.cur"
+arrow CURSOR DISCARDABLE "cursor02.cur"
+based_arrow_down CURSOR DISCARDABLE "cursor04.cur"
+based_arrow_up CURSOR DISCARDABLE "cursor06.cur"
+boat CURSOR DISCARDABLE "cursor08.cur"
+bogosity CURSOR DISCARDABLE "cursor0a.cur"
+bottom_left_corner CURSOR DISCARDABLE "cursor0c.cur"
+bottom_right_corner CURSOR DISCARDABLE "cursor0e.cur"
+bottom_side CURSOR DISCARDABLE "cursor10.cur"
+bottom_tee CURSOR DISCARDABLE "cursor12.cur"
+box_spiral CURSOR DISCARDABLE "cursor14.cur"
+center_ptr CURSOR DISCARDABLE "cursor16.cur"
+circle CURSOR DISCARDABLE "cursor18.cur"
+clock CURSOR DISCARDABLE "cursor1a.cur"
+coffee_mug CURSOR DISCARDABLE "cursor1c.cur"
+cross CURSOR DISCARDABLE "cursor1e.cur"
+cross_reverse CURSOR DISCARDABLE "cursor20.cur"
+crosshair CURSOR DISCARDABLE "cursor22.cur"
+diamond_cross CURSOR DISCARDABLE "cursor24.cur"
+dot CURSOR DISCARDABLE "cursor26.cur"
+dotbox CURSOR DISCARDABLE "cursor28.cur"
+double_arrow CURSOR DISCARDABLE "cursor2a.cur"
+draft_large CURSOR DISCARDABLE "cursor2c.cur"
+draft_small CURSOR DISCARDABLE "cursor2e.cur"
+draped_box CURSOR DISCARDABLE "cursor30.cur"
+exchange CURSOR DISCARDABLE "cursor32.cur"
+fleur CURSOR DISCARDABLE "cursor34.cur"
+gobbler CURSOR DISCARDABLE "cursor36.cur"
+gumby CURSOR DISCARDABLE "cursor38.cur"
+hand1 CURSOR DISCARDABLE "cursor3a.cur"
+hand2 CURSOR DISCARDABLE "cursor3c.cur"
+heart CURSOR DISCARDABLE "cursor3e.cur"
+icon CURSOR DISCARDABLE "cursor40.cur"
+iron_cross CURSOR DISCARDABLE "cursor42.cur"
+left_ptr CURSOR DISCARDABLE "cursor44.cur"
+left_side CURSOR DISCARDABLE "cursor46.cur"
+left_tee CURSOR DISCARDABLE "cursor48.cur"
+leftbutton CURSOR DISCARDABLE "cursor4a.cur"
+ll_angle CURSOR DISCARDABLE "cursor4c.cur"
+lr_angle CURSOR DISCARDABLE "cursor4e.cur"
+man CURSOR DISCARDABLE "cursor50.cur"
+middlebutton CURSOR DISCARDABLE "cursor52.cur"
+mouse CURSOR DISCARDABLE "cursor54.cur"
+pencil CURSOR DISCARDABLE "cursor56.cur"
+pirate CURSOR DISCARDABLE "cursor58.cur"
+plus CURSOR DISCARDABLE "cursor5a.cur"
+question_arrow CURSOR DISCARDABLE "cursor5c.cur"
+right_ptr CURSOR DISCARDABLE "cursor5e.cur"
+right_side CURSOR DISCARDABLE "cursor60.cur"
+right_tee CURSOR DISCARDABLE "cursor62.cur"
+rightbutton CURSOR DISCARDABLE "cursor64.cur"
+rtl_logo CURSOR DISCARDABLE "cursor66.cur"
+sailboat CURSOR DISCARDABLE "cursor68.cur"
+sb_down_arrow CURSOR DISCARDABLE "cursor6a.cur"
+sb_h_double_arrow CURSOR DISCARDABLE "cursor6c.cur"
+sb_left_arrow CURSOR DISCARDABLE "cursor6e.cur"
+sb_right_arrow CURSOR DISCARDABLE "cursor70.cur"
+sb_up_arrow CURSOR DISCARDABLE "cursor72.cur"
+sb_v_double_arrow CURSOR DISCARDABLE "cursor74.cur"
+shuttle CURSOR DISCARDABLE "cursor76.cur"
+sizing CURSOR DISCARDABLE "cursor78.cur"
+spider CURSOR DISCARDABLE "cursor7a.cur"
+spraycan CURSOR DISCARDABLE "cursor7c.cur"
+star CURSOR DISCARDABLE "cursor7e.cur"
+target CURSOR DISCARDABLE "cursor80.cur"
+tcross CURSOR DISCARDABLE "cursor82.cur"
+top_left_arrow CURSOR DISCARDABLE "cursor84.cur"
+top_left_corner CURSOR DISCARDABLE "cursor86.cur"
+top_right_corner CURSOR DISCARDABLE "cursor88.cur"
+top_side CURSOR DISCARDABLE "cursor8a.cur"
+top_tee CURSOR DISCARDABLE "cursor8c.cur"
+trek CURSOR DISCARDABLE "cursor8e.cur"
+ul_angle CURSOR DISCARDABLE "cursor90.cur"
+umbrella CURSOR DISCARDABLE "cursor92.cur"
+ur_angle CURSOR DISCARDABLE "cursor94.cur"
+watch CURSOR DISCARDABLE "cursor96.cur"
+xterm CURSOR DISCARDABLE "cursor98.cur"
diff --git a/win/rc/wish.ico b/win/rc/wish.ico
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/rc/wish.ico
diff --git a/win/rc/wish.rc b/win/rc/wish.rc
new file mode 100644
index 0000000..76cf124
--- /dev/null
+++ b/win/rc/wish.rc
@@ -0,0 +1,44 @@
+// SCCS: @(#) wish.rc 1.15 96/09/17 13:24:11
+//
+// Version
+//
+
+#define RESOURCE_INCLUDED
+#include <tk.h>
+
+#define STRINGIFY1(x) #x
+#define STRINGIFY(x) STRINGIFY1(x)
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+ PRODUCTVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0x0L
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Wish Application\0"
+ VALUE "OriginalFilename", "wish" STRINGIFY(TK_MAJOR_VERSION) STRINGIFY(TK_MINOR_VERSION) ".exe\0"
+ VALUE "CompanyName", "Sun Microsystems, Inc.\0"
+ VALUE "FileVersion", TK_PATCH_LEVEL
+ VALUE "LegalCopyright", "Copyright \251 1995-1996\0"
+ VALUE "ProductName", "Tk " TK_VERSION " for Windows\0"
+ VALUE "ProductVersion", TK_PATCH_LEVEL
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+//
+// Icon
+//
+
+wish ICON DISCARDABLE "wish.ico"
diff --git a/win/stubs.c b/win/stubs.c
new file mode 100644
index 0000000..c9b97f5
--- /dev/null
+++ b/win/stubs.c
@@ -0,0 +1,397 @@
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <stdio.h>
+#include <tkInt.h>
+#include <tkPort.h>
+
+/*
+ * Undocumented Xlib internal function
+ */
+
+_XInitImageFuncPtrs(XImage *image)
+{
+ return 0;
+}
+
+/*
+ * From Xutil.h
+ */
+
+void
+XSetWMClientMachine(display, w, text_prop)
+ Display* display;
+ Window w;
+ XTextProperty* text_prop;
+{
+}
+
+Status
+XStringListToTextProperty(list, count, text_prop_return)
+ char** list;
+ int count;
+ XTextProperty* text_prop_return;
+{
+ return (Status) NULL;
+}
+
+/*
+ * From Xlib.h
+ */
+
+void
+XChangeProperty(display, w, property, type, format, mode, data, nelements)
+ Display* display;
+ Window w;
+ Atom property;
+ Atom type;
+ int format;
+ int mode;
+ _Xconst unsigned char* data;
+ int nelements;
+{
+}
+
+Cursor
+XCreateGlyphCursor(display, source_font, mask_font, source_char, mask_char,
+ foreground_color, background_color)
+ Display* display;
+ Font source_font;
+ Font mask_font;
+ unsigned int source_char;
+ unsigned int mask_char;
+ XColor* foreground_color;
+ XColor* background_color;
+{
+ return 1;
+}
+
+XIC
+XCreateIC()
+{
+ return NULL;
+}
+
+Cursor
+XCreatePixmapCursor(display, source, mask, foreground_color,
+ background_color, x, y)
+ Display* display;
+ Pixmap source;
+ Pixmap mask;
+ XColor* foreground_color;
+ XColor* background_color;
+ unsigned int x;
+ unsigned int y;
+{
+ return (Cursor) NULL;
+}
+
+void
+XDeleteProperty(display, w, property)
+ Display* display;
+ Window w;
+ Atom property;
+{
+}
+
+void
+XDestroyIC(ic)
+ XIC ic;
+{
+}
+
+Bool
+XFilterEvent(event, window)
+ XEvent* event;
+ Window window;
+{
+ return 0;
+}
+
+extern void XForceScreenSaver(display, mode)
+ Display* display;
+ int mode;
+{
+}
+
+void
+XFreeCursor(display, cursor)
+ Display* display;
+ Cursor cursor;
+{
+}
+
+GContext
+XGContextFromGC(gc)
+ GC gc;
+{
+ return (GContext) NULL;
+}
+
+char *
+XGetAtomName(display, atom)
+ Display* display;
+ Atom atom;
+{
+ return NULL;
+}
+
+int
+XGetWindowAttributes(display, w, window_attributes_return)
+ Display* display;
+ Window w;
+ XWindowAttributes* window_attributes_return;
+{
+ return 0;
+}
+
+Status
+XGetWMColormapWindows(display, w, windows_return, count_return)
+ Display* display;
+ Window w;
+ Window** windows_return;
+ int* count_return;
+{
+ return (Status) NULL;
+}
+
+int
+XIconifyWindow(display, w, screen_number)
+ Display* display;
+ Window w;
+ int screen_number;
+{
+ return 0;
+}
+
+XHostAddress *
+XListHosts(display, nhosts_return, state_return)
+ Display* display;
+ int* nhosts_return;
+ Bool* state_return;
+{
+ return NULL;
+}
+
+int
+XLookupColor(display, colormap, color_name, exact_def_return,
+ screen_def_return)
+ Display* display;
+ Colormap colormap;
+ _Xconst char* color_name;
+ XColor* exact_def_return;
+ XColor* screen_def_return;
+{
+ return 0;
+}
+
+void
+XNextEvent(display, event_return)
+ Display* display;
+ XEvent* event_return;
+{
+}
+
+void
+XPutBackEvent(display, event)
+ Display* display;
+ XEvent* event;
+{
+}
+
+void
+XQueryColors(display, colormap, defs_in_out, ncolors)
+ Display* display;
+ Colormap colormap;
+ XColor* defs_in_out;
+ int ncolors;
+{
+}
+
+int
+XQueryTree(display, w, root_return, parent_return, children_return,
+ nchildren_return)
+ Display* display;
+ Window w;
+ Window* root_return;
+ Window* parent_return;
+ Window** children_return;
+ unsigned int* nchildren_return;
+{
+ return 0;
+}
+
+void
+XRefreshKeyboardMapping(event_map)
+ XMappingEvent* event_map;
+{
+}
+
+Window
+XRootWindow(display, screen_number)
+ Display* display;
+ int screen_number;
+{
+ return (Window) NULL;
+}
+
+void
+XSelectInput(display, w, event_mask)
+ Display* display;
+ Window w;
+ long event_mask;
+{
+}
+
+int
+XSendEvent(display, w, propagate, event_mask, event_send)
+ Display* display;
+ Window w;
+ Bool propagate;
+ long event_mask;
+ XEvent* event_send;
+{
+ return 0;
+}
+
+void
+XSetCommand(display, w, argv, argc)
+ Display* display;
+ Window w;
+ char** argv;
+ int argc;
+{
+}
+
+XErrorHandler
+XSetErrorHandler (handler)
+ XErrorHandler handler;
+{
+ return NULL;
+}
+
+void
+XSetIconName(display, w, icon_name)
+ Display* display;
+ Window w;
+ _Xconst char* icon_name;
+{
+}
+
+void
+XSetWindowBackground(display, w, background_pixel)
+ Display* display;
+ Window w;
+ unsigned long background_pixel;
+{
+}
+
+void
+XSetWindowBackgroundPixmap(display, w, background_pixmap)
+ Display* display;
+ Window w;
+ Pixmap background_pixmap;
+{
+}
+
+void
+XSetWindowBorder(display, w, border_pixel)
+ Display* display;
+ Window w;
+ unsigned long border_pixel;
+{
+}
+
+void
+XSetWindowBorderPixmap(display, w, border_pixmap)
+ Display* display;
+ Window w;
+ Pixmap border_pixmap;
+{
+}
+
+void
+XSetWindowBorderWidth(display, w, width)
+ Display* display;
+ Window w;
+ unsigned int width;
+{
+}
+
+void
+XSetWindowColormap(display, w, colormap)
+ Display* display;
+ Window w;
+ Colormap colormap;
+{
+}
+
+Bool
+XTranslateCoordinates(display, src_w, dest_w, src_x, src_y, dest_x_return,
+ dest_y_return, child_return)
+ Display* display;
+ Window src_w;
+ Window dest_w;
+ int src_x;
+ int src_y;
+ int* dest_x_return;
+ int* dest_y_return;
+ Window* child_return;
+{
+ return 0;
+}
+
+void
+XWindowEvent(display, w, event_mask, event_return)
+ Display* display;
+ Window w;
+ long event_mask;
+ XEvent* event_return;
+{
+}
+
+int
+XWithdrawWindow(display, w, screen_number)
+ Display* display;
+ Window w;
+ int screen_number;
+{
+ return 0;
+}
+
+int
+XmbLookupString(ic, event, buffer_return, bytes_buffer, keysym_return,
+ status_return)
+ XIC ic;
+ XKeyPressedEvent* event;
+ char* buffer_return;
+ int bytes_buffer;
+ KeySym* keysym_return;
+ Status* status_return;
+{
+ return 0;
+}
+
+int
+XGetWindowProperty(display, w, property, long_offset, long_length, delete,
+ req_type, actual_type_return, actual_format_return, nitems_return,
+ bytes_after_return, prop_return)
+ Display* display;
+ Window w;
+ Atom property;
+ long long_offset;
+ long long_length;
+ Bool delete;
+ Atom req_type;
+ Atom* actual_type_return;
+ int* actual_format_return;
+ unsigned long* nitems_return;
+ unsigned long* bytes_after_return;
+ unsigned char** prop_return;
+{
+ *actual_type_return = None;
+ *actual_format_return = 0;
+ *nitems_return = 0;
+ *bytes_after_return = 0;
+ *prop_return = NULL;
+ return BadValue;
+}
diff --git a/win/tkWin.h b/win/tkWin.h
new file mode 100644
index 0000000..c9d9360
--- /dev/null
+++ b/win/tkWin.h
@@ -0,0 +1,56 @@
+/*
+ * tkWin.h --
+ *
+ * Declarations of public types and interfaces that are only
+ * available under Windows.
+ *
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWin.h 1.6 96/08/15 13:19:41
+ */
+
+#ifndef _TKWIN
+#define _TKWIN
+
+#ifndef _TK
+#include <tk.h>
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+/*
+ * The following messages are use to communicate between a Tk toplevel
+ * and its container window.
+ */
+
+#define TK_CLAIMFOCUS (WM_USER)
+#define TK_GEOMETRYREQ (WM_USER+1)
+#define TK_ATTACHWINDOW (WM_USER+2)
+#define TK_DETACHWINDOW (WM_USER+3)
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Exported procedures defined for the Windows platform only.
+ *
+ *--------------------------------------------------------------
+ */
+
+EXTERN Window Tk_AttachHWND _ANSI_ARGS_((Tk_Window tkwin,
+ HWND hwnd));
+EXTERN HINSTANCE Tk_GetHINSTANCE _ANSI_ARGS_((void));
+EXTERN HWND Tk_GetHWND _ANSI_ARGS_((Window window));
+EXTERN Tk_Window Tk_HWNDToWindow _ANSI_ARGS_((HWND hwnd));
+EXTERN void Tk_PointerEvent _ANSI_ARGS_((HWND hwnd,
+ int x, int y));
+EXTERN int Tk_TranslateWinEvent _ANSI_ARGS_((HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam,
+ LRESULT *result));
+
+#endif /* _TKWIN */
diff --git a/win/tkWin32Dll.c b/win/tkWin32Dll.c
new file mode 100644
index 0000000..969e687
--- /dev/null
+++ b/win/tkWin32Dll.c
@@ -0,0 +1,85 @@
+/*
+ * tkWin32Dll.c --
+ *
+ * This file contains a stub dll entry point.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWin32Dll.c 1.9 96/08/06 15:59:08
+ */
+
+#include "tkPort.h"
+#include "tkWinInt.h"
+
+/*
+ * The following declaration is for the VC++ DLL entry point.
+ */
+
+BOOL APIENTRY DllMain _ANSI_ARGS_((HINSTANCE hInst,
+ DWORD reason, LPVOID reserved));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DllEntryPoint --
+ *
+ * This wrapper function is used by Borland to invoke the
+ * initialization code for Tk. It simply calls the DllMain
+ * routine.
+ *
+ * Results:
+ * See DllMain.
+ *
+ * Side effects:
+ * See DllMain.
+ *
+ *----------------------------------------------------------------------
+ */
+
+BOOL APIENTRY
+DllEntryPoint(hInst, reason, reserved)
+ HINSTANCE hInst; /* Library instance handle. */
+ DWORD reason; /* Reason this function is being called. */
+ LPVOID reserved; /* Not used. */
+{
+ return DllMain(hInst, reason, reserved);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DllMain --
+ *
+ * DLL entry point.
+ *
+ * Results:
+ * TRUE on sucess, FALSE on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+BOOL APIENTRY
+DllMain(hInstance, reason, reserved)
+ HINSTANCE hInstance;
+ DWORD reason;
+ LPVOID reserved;
+{
+ /*
+ * If we are attaching to the DLL from a new process, tell Tk about
+ * the hInstance to use. If we are detaching then clean up any
+ * data structures related to this DLL.
+ */
+
+ if (reason == DLL_PROCESS_ATTACH) {
+ TkWinXInit(hInstance);
+ } else if (reason == DLL_PROCESS_DETACH) {
+ TkWinXCleanup(hInstance);
+ }
+ return(TRUE);
+}
diff --git a/win/tkWin3d.c b/win/tkWin3d.c
new file mode 100644
index 0000000..3ee9907
--- /dev/null
+++ b/win/tkWin3d.c
@@ -0,0 +1,535 @@
+/*
+ * tkWin3d.c --
+ *
+ * This file contains the platform specific routines for
+ * drawing 3d borders in the Windows 95 style.
+ *
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWin3d.c 1.6 97/08/12 14:28:54
+ */
+
+#include <tk3d.h>
+#include <tkWinInt.h>
+
+/*
+ * This structure is used to keep track of the extra colors used by
+ * Windows 3d borders.
+ */
+
+typedef struct {
+ TkBorder info;
+ XColor *light2ColorPtr; /* System3dLight */
+ XColor *dark2ColorPtr; /* System3dDarkShadow */
+} WinBorder;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetBorder --
+ *
+ * This function allocates a new TkBorder structure.
+ *
+ * Results:
+ * Returns a newly allocated TkBorder.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkBorder *
+TkpGetBorder()
+{
+ WinBorder *borderPtr = (WinBorder *) ckalloc(sizeof(WinBorder));
+ borderPtr->light2ColorPtr = NULL;
+ borderPtr->dark2ColorPtr = NULL;
+ return (TkBorder *) borderPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpFreeBorder --
+ *
+ * This function frees any colors allocated by the platform
+ * specific part of this module.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May deallocate some colors.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpFreeBorder(borderPtr)
+ TkBorder *borderPtr;
+{
+ WinBorder *winBorderPtr = (WinBorder *) borderPtr;
+ if (winBorderPtr->light2ColorPtr) {
+ Tk_FreeColor(winBorderPtr->light2ColorPtr);
+ }
+ if (winBorderPtr->dark2ColorPtr) {
+ Tk_FreeColor(winBorderPtr->dark2ColorPtr);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_3DVerticalBevel --
+ *
+ * This procedure draws a vertical bevel along one side of
+ * an object. The bevel is always rectangular in shape:
+ * |||
+ * |||
+ * |||
+ * |||
+ * |||
+ * |||
+ * An appropriate shadow color is chosen for the bevel based
+ * on the leftBevel and relief arguments. Normally this
+ * procedure is called first, then Tk_3DHorizontalBevel is
+ * called next to draw neat corners.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Graphics are drawn in drawable.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_3DVerticalBevel(tkwin, drawable, border, x, y, width, height,
+ leftBevel, relief)
+ Tk_Window tkwin; /* Window for which border was allocated. */
+ Drawable drawable; /* X window or pixmap in which to draw. */
+ Tk_3DBorder border; /* Token for border to draw. */
+ int x, y, width, height; /* Area of vertical bevel. */
+ int leftBevel; /* Non-zero means this bevel forms the
+ * left side of the object; 0 means it
+ * forms the right side. */
+ int relief; /* Kind of bevel to draw. For example,
+ * TK_RELIEF_RAISED means interior of
+ * object should appear higher than
+ * exterior. */
+{
+ TkBorder *borderPtr = (TkBorder *) border;
+ int left, right;
+ Display *display = Tk_Display(tkwin);
+ TkWinDCState state;
+ HDC dc = TkWinGetDrawableDC(display, drawable, &state);
+ int half;
+
+ if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
+ TkpGetShadows(borderPtr, tkwin);
+ }
+
+ switch (relief) {
+ case TK_RELIEF_RAISED:
+ left = (leftBevel)
+ ? borderPtr->lightGC->foreground
+ : borderPtr->darkGC->foreground;
+ right = (leftBevel)
+ ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel
+ : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel;
+ break;
+ case TK_RELIEF_SUNKEN:
+ left = (leftBevel)
+ ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel
+ : ((WinBorder *)borderPtr)->light2ColorPtr->pixel;
+ right = (leftBevel)
+ ? borderPtr->darkGC->foreground
+ : borderPtr->lightGC->foreground;
+ break;
+ case TK_RELIEF_RIDGE:
+ left = borderPtr->lightGC->foreground;
+ right = borderPtr->darkGC->foreground;
+ break;
+ case TK_RELIEF_GROOVE:
+ left = borderPtr->darkGC->foreground;
+ right = borderPtr->lightGC->foreground;
+ break;
+ case TK_RELIEF_FLAT:
+ left = right = borderPtr->bgGC->foreground;
+ break;
+ case TK_RELIEF_SOLID:
+ left = right = RGB(0,0,0);
+ break;
+ }
+ half = width/2;
+ if (leftBevel && (width & 1)) {
+ half++;
+ }
+ TkWinFillRect(dc, x, y, half, height, left);
+ TkWinFillRect(dc, x+half, y, width-half, height, right);
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_3DHorizontalBevel --
+ *
+ * This procedure draws a horizontal bevel along one side of
+ * an object. The bevel has mitered corners (depending on
+ * leftIn and rightIn arguments).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+Tk_3DHorizontalBevel(tkwin, drawable, border, x, y, width, height,
+ leftIn, rightIn, topBevel, relief)
+ Tk_Window tkwin; /* Window for which border was allocated. */
+ Drawable drawable; /* X window or pixmap in which to draw. */
+ Tk_3DBorder border; /* Token for border to draw. */
+ int x, y, width, height; /* Bounding box of area of bevel. Height
+ * gives width of border. */
+ int leftIn, rightIn; /* Describes whether the left and right
+ * edges of the bevel angle in or out as
+ * they go down. For example, if "leftIn"
+ * is true, the left side of the bevel
+ * looks like this:
+ * ___________
+ * __________
+ * _________
+ * ________
+ */
+ int topBevel; /* Non-zero means this bevel forms the
+ * top side of the object; 0 means it
+ * forms the bottom side. */
+ int relief; /* Kind of bevel to draw. For example,
+ * TK_RELIEF_RAISED means interior of
+ * object should appear higher than
+ * exterior. */
+{
+ TkBorder *borderPtr = (TkBorder *) border;
+ Display *display = Tk_Display(tkwin);
+ int bottom, halfway, x1, x2, x1Delta, x2Delta;
+ TkWinDCState state;
+ HDC dc = TkWinGetDrawableDC(display, drawable, &state);
+ int topColor, bottomColor;
+
+ if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
+ TkpGetShadows(borderPtr, tkwin);
+ }
+
+ /*
+ * Compute a GC for the top half of the bevel and a GC for the
+ * bottom half (they're the same in many cases).
+ */
+
+ switch (relief) {
+ case TK_RELIEF_RAISED:
+ topColor = (topBevel)
+ ? borderPtr->lightGC->foreground
+ : borderPtr->darkGC->foreground;
+ bottomColor = (topBevel)
+ ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel
+ : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel;
+ break;
+ case TK_RELIEF_SUNKEN:
+ topColor = (topBevel)
+ ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel
+ : ((WinBorder *)borderPtr)->light2ColorPtr->pixel;
+ bottomColor = (topBevel)
+ ? borderPtr->darkGC->foreground
+ : borderPtr->lightGC->foreground;
+ break;
+ case TK_RELIEF_RIDGE:
+ topColor = borderPtr->lightGC->foreground;
+ bottomColor = borderPtr->darkGC->foreground;
+ break;
+ case TK_RELIEF_GROOVE:
+ topColor = borderPtr->darkGC->foreground;
+ bottomColor = borderPtr->lightGC->foreground;
+ break;
+ case TK_RELIEF_FLAT:
+ topColor = bottomColor = borderPtr->bgGC->foreground;
+ break;
+ case TK_RELIEF_SOLID:
+ topColor = bottomColor = RGB(0,0,0);
+ }
+
+ /*
+ * Compute various other geometry-related stuff.
+ */
+
+ if (leftIn) {
+ x1 = x+1;
+ } else {
+ x1 = x+height-1;
+ }
+ x2 = x+width;
+ if (rightIn) {
+ x2--;
+ } else {
+ x2 -= height;
+ }
+ x1Delta = (leftIn) ? 1 : -1;
+ x2Delta = (rightIn) ? -1 : 1;
+ halfway = y + height/2;
+ if (topBevel && (height & 1)) {
+ halfway++;
+ }
+ bottom = y + height;
+
+ /*
+ * Draw one line for each y-coordinate covered by the bevel.
+ */
+
+ for ( ; y < bottom; y++) {
+ /*
+ * In some weird cases (such as large border widths for skinny
+ * rectangles) x1 can be >= x2. Don't draw the lines
+ * in these cases.
+ */
+
+ if (x1 < x2) {
+ TkWinFillRect(dc, x1, y, x2-x1, 1,
+ (y < halfway) ? topColor : bottomColor);
+ }
+ x1 += x1Delta;
+ x2 += x2Delta;
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetShadows --
+ *
+ * This procedure computes the shadow colors for a 3-D border
+ * and fills in the corresponding fields of the Border structure.
+ * It's called lazily, so that the colors aren't allocated until
+ * something is actually drawn with them. That way, if a border
+ * is only used for flat backgrounds the shadow colors will
+ * never be allocated.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The lightGC and darkGC fields in borderPtr get filled in,
+ * if they weren't already.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpGetShadows(borderPtr, tkwin)
+ TkBorder *borderPtr; /* Information about border. */
+ Tk_Window tkwin; /* Window where border will be used for
+ * drawing. */
+{
+ XColor lightColor, darkColor;
+ int tmp1, tmp2;
+ XGCValues gcValues;
+
+ if (borderPtr->lightGC != None) {
+ return;
+ }
+
+ /*
+ * Handle the special case of the default system colors.
+ */
+
+ if ((TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_3DFACE)
+ || (TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_WINDOW)) {
+ borderPtr->darkColorPtr = Tk_GetColor(NULL, tkwin,
+ Tk_GetUid("SystemButtonShadow"));
+ gcValues.foreground = borderPtr->darkColorPtr->pixel;
+ borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ borderPtr->lightColorPtr = Tk_GetColor(NULL, tkwin,
+ Tk_GetUid("SystemButtonHighlight"));
+ gcValues.foreground = borderPtr->lightColorPtr->pixel;
+ borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColor(NULL, tkwin,
+ Tk_GetUid("System3dDarkShadow"));
+ ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColor(NULL, tkwin,
+ Tk_GetUid("System3dLight"));
+ return;
+ } else {
+ darkColor.red = 0;
+ darkColor.green = 0;
+ darkColor.blue = 0;
+ ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColorByValue(tkwin,
+ &darkColor);
+ lightColor = *(borderPtr->bgColorPtr);
+ ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColorByValue(tkwin,
+ &lightColor);
+ }
+
+ /*
+ * First, handle the case of a color display with lots of colors.
+ * The shadow colors get computed using whichever formula results
+ * in the greatest change in color:
+ * 1. Lighter shadow is half-way to white, darker shadow is half
+ * way to dark.
+ * 2. Lighter shadow is 40% brighter than background, darker shadow
+ * is 40% darker than background.
+ */
+
+ if (Tk_Depth(tkwin) >= 6) {
+ /*
+ * This is a color display with lots of colors. For the dark
+ * shadow, cut 40% from each of the background color components.
+ * For the light shadow, boost each component by 40% or half-way
+ * to white, whichever is greater (the first approach works
+ * better for unsaturated colors, the second for saturated ones).
+ */
+
+ darkColor.red = (60 * (int) borderPtr->bgColorPtr->red)/100;
+ darkColor.green = (60 * (int) borderPtr->bgColorPtr->green)/100;
+ darkColor.blue = (60 * (int) borderPtr->bgColorPtr->blue)/100;
+ borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
+ gcValues.foreground = borderPtr->darkColorPtr->pixel;
+ borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+
+ /*
+ * Compute the colors using integers, not using lightColor.red
+ * etc.: these are shorts and may have problems with integer
+ * overflow.
+ */
+
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->red)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->red)/2;
+ lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->green)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->green)/2;
+ lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->blue)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->blue)/2;
+ lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
+ borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor);
+ gcValues.foreground = borderPtr->lightColorPtr->pixel;
+ borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ return;
+ }
+
+ if (borderPtr->shadow == None) {
+ borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin,
+ Tk_GetUid("gray50"));
+ if (borderPtr->shadow == None) {
+ panic("TkpGetShadows couldn't allocate bitmap for border");
+ }
+ }
+ if (borderPtr->visual->map_entries > 2) {
+ /*
+ * This isn't a monochrome display, but the colormap either
+ * ran out of entries or didn't have very many to begin with.
+ * Generate the light shadows with a white stipple and the
+ * dark shadows with a black stipple.
+ */
+
+ gcValues.foreground = borderPtr->bgColorPtr->pixel;
+ gcValues.background = BlackPixelOfScreen(borderPtr->screen);
+ gcValues.stipple = borderPtr->shadow;
+ gcValues.fill_style = FillOpaqueStippled;
+ borderPtr->darkGC = Tk_GetGC(tkwin,
+ GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
+ gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
+ gcValues.background = borderPtr->bgColorPtr->pixel;
+ borderPtr->lightGC = Tk_GetGC(tkwin,
+ GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
+ return;
+ }
+
+ /*
+ * This is just a measly monochrome display, hardly even worth its
+ * existence on this earth. Make one shadow a 50% stipple and the
+ * other the opposite of the background.
+ */
+
+ gcValues.foreground = WhitePixelOfScreen(borderPtr->screen);
+ gcValues.background = BlackPixelOfScreen(borderPtr->screen);
+ gcValues.stipple = borderPtr->shadow;
+ gcValues.fill_style = FillOpaqueStippled;
+ borderPtr->lightGC = Tk_GetGC(tkwin,
+ GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues);
+ if (borderPtr->bgColorPtr->pixel
+ == WhitePixelOfScreen(borderPtr->screen)) {
+ gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
+ borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ } else {
+ borderPtr->darkGC = borderPtr->lightGC;
+ borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetBorderPixels --
+ *
+ * This routine returns the 5 COLORREFs used to draw a given
+ * 3d border.
+ *
+ * Results:
+ * Returns the colors in the specified array.
+ *
+ * Side effects:
+ * May cause the remaining colors to be allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+COLORREF
+TkWinGetBorderPixels(tkwin, border, which)
+ Tk_Window tkwin;
+ Tk_3DBorder border;
+ int which; /* One of TK_3D_FLAT_GC, TK_3D_LIGHT_GC,
+ * TK_3D_DARK_GC, TK_3D_LIGHT2, TK_3D_DARK2 */
+{
+ WinBorder *borderPtr = (WinBorder *) border;
+
+ if (borderPtr->info.lightGC == None) {
+ TkpGetShadows(&borderPtr->info, tkwin);
+ }
+ switch (which) {
+ case TK_3D_FLAT_GC:
+ return borderPtr->info.bgColorPtr->pixel;
+ case TK_3D_LIGHT_GC:
+ if (borderPtr->info.lightColorPtr == NULL) {
+ return WhitePixelOfScreen(borderPtr->info.screen);
+ }
+ return borderPtr->info.lightColorPtr->pixel;
+ case TK_3D_DARK_GC:
+ if (borderPtr->info.darkColorPtr == NULL) {
+ return BlackPixelOfScreen(borderPtr->info.screen);
+ }
+ return borderPtr->info.darkColorPtr->pixel;
+ case TK_3D_LIGHT2:
+ return borderPtr->light2ColorPtr->pixel;
+ case TK_3D_DARK2:
+ return borderPtr->dark2ColorPtr->pixel;
+ }
+ return 0;
+}
diff --git a/win/tkWinButton.c b/win/tkWinButton.c
new file mode 100644
index 0000000..47a74e6
--- /dev/null
+++ b/win/tkWinButton.c
@@ -0,0 +1,811 @@
+/*
+ * tkWinButton.c --
+ *
+ * This file implements the Windows specific portion of the button
+ * widgets.
+ *
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinButton.c 1.12 97/09/02 13:18:27
+ */
+
+#define OEMRESOURCE
+#include "tkWinInt.h"
+#include "tkButton.h"
+
+/*
+ * These macros define the base style flags for the different button types.
+ */
+
+#define LABEL_STYLE (BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
+#define PUSH_STYLE (BS_OWNERDRAW | BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
+#define CHECK_STYLE (BS_OWNERDRAW | BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
+#define RADIO_STYLE (BS_OWNERDRAW | BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
+
+static DWORD buttonStyles[] = {
+ LABEL_STYLE, PUSH_STYLE, CHECK_STYLE, RADIO_STYLE
+};
+
+/*
+ * Declaration of Windows specific button structure.
+ */
+
+typedef struct WinButton {
+ TkButton info; /* Generic button info. */
+ WNDPROC oldProc; /* Old window procedure. */
+ HWND hwnd; /* Current window handle. */
+ Pixmap pixmap; /* Bitmap for rendering the button. */
+ DWORD style; /* Window style flags. */
+} WinButton;
+
+
+/*
+ * The following macro reverses the order of RGB bytes to convert
+ * between RGBQUAD and COLORREF values.
+ */
+
+#define FlipColor(rgb) (RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb)))
+
+/*
+ * The following enumeration defines the meaning of the palette entries
+ * in the "buttons" image used to draw checkbox and radiobutton indicators.
+ */
+
+enum {
+ PAL_CHECK = 0,
+ PAL_TOP_OUTER = 1,
+ PAL_BOTTOM_OUTER = 2,
+ PAL_BOTTOM_INNER = 3,
+ PAL_INTERIOR = 4,
+ PAL_TOP_INNER = 5,
+ PAL_BACKGROUND = 6
+};
+
+/*
+ * Set to non-zero if this module is initialized.
+ */
+
+static int initialized = 0;
+
+/*
+ * Variables for the cached information about the boxes bitmap.
+ */
+
+static BITMAPINFOHEADER *boxesPtr = NULL; /* Information about the bitmap. */
+static DWORD *boxesPalette = NULL; /* Pointer to color palette. */
+static LPSTR boxesBits = NULL; /* Pointer to bitmap data. */
+static DWORD boxHeight = 0, boxWidth = 0; /* Size of each sub-image. */
+
+/*
+ * This variable holds the default border width for a button in string
+ * form for use in a Tk_ConfigSpec.
+ */
+
+static char defWidth[8];
+
+/*
+ * Declarations for functions defined in this file.
+ */
+
+static int ButtonBindProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, XEvent *eventPtr,
+ Tk_Window tkwin, KeySym keySym));
+static LRESULT CALLBACK ButtonProc _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+static DWORD ComputeStyle _ANSI_ARGS_((WinButton* butPtr));
+static Window CreateProc _ANSI_ARGS_((Tk_Window tkwin,
+ Window parent, ClientData instanceData));
+static void InitBoxes _ANSI_ARGS_((void));
+static void UpdateButtonDefaults _ANSI_ARGS_((void));
+
+/*
+ * The class procedure table for the button widgets.
+ */
+
+TkClassProcs tkpButtonProcs = {
+ CreateProc, /* createProc. */
+ TkButtonWorldChanged, /* geometryProc. */
+ NULL /* modalProc. */
+};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitBoxes --
+ *
+ * This function load the Tk 3d button bitmap. "buttons" is a 16
+ * color bitmap that is laid out such that the top row contains
+ * the 4 checkbox images, and the bottom row contains the radio
+ * button images. Note that the bitmap is stored in bottom-up
+ * format. Also, the first seven palette entries are used to
+ * identify the different parts of the bitmaps so we can do the
+ * appropriate color mappings based on the current button colors.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Loads the "buttons" resource.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InitBoxes()
+{
+ /*
+ * For DLLs like Tk, the HINSTANCE is the same as the HMODULE.
+ */
+
+ HMODULE module = (HINSTANCE) Tk_GetHINSTANCE();
+ HRSRC hrsrc;
+ HGLOBAL hblk;
+ LPBITMAPINFOHEADER newBitmap;
+ DWORD size;
+
+ hrsrc = FindResource(module, "buttons", RT_BITMAP);
+ if (hrsrc) {
+ hblk = LoadResource(module, hrsrc);
+ boxesPtr = (LPBITMAPINFOHEADER)LockResource(hblk);
+ }
+
+ /*
+ * Copy the DIBitmap into writable memory.
+ */
+
+ if (boxesPtr != NULL && !(boxesPtr->biWidth % 4)
+ && !(boxesPtr->biHeight % 2)) {
+ size = boxesPtr->biSize + (1 << boxesPtr->biBitCount) * sizeof(RGBQUAD)
+ + boxesPtr->biSizeImage;
+ newBitmap = (LPBITMAPINFOHEADER) ckalloc(size);
+ memcpy(newBitmap, boxesPtr, size);
+ boxesPtr = newBitmap;
+ boxWidth = boxesPtr->biWidth / 4;
+ boxHeight = boxesPtr->biHeight / 2;
+ boxesPalette = (DWORD*) (((LPSTR)boxesPtr) + boxesPtr->biSize);
+ boxesBits = ((LPSTR)boxesPalette)
+ + ((1 << boxesPtr->biBitCount) * sizeof(RGBQUAD));
+ } else {
+ boxesPtr = NULL;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateButtonDefaults --
+ *
+ * This function retrieves the current system defaults for
+ * the button widgets.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Updates the configuration defaults for buttons.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+UpdateButtonDefaults()
+{
+ Tk_ConfigSpec *specPtr;
+ int width = GetSystemMetrics(SM_CXEDGE);
+
+ if (width == 0) {
+ width = 1;
+ }
+ sprintf(defWidth, "%d", width);
+ for (specPtr = tkpButtonConfigSpecs; specPtr->type != TK_CONFIG_END;
+ specPtr++) {
+ if (specPtr->offset == Tk_Offset(TkButton, borderWidth)) {
+ specPtr->defValue = defWidth;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateButton --
+ *
+ * Allocate a new TkButton structure.
+ *
+ * Results:
+ * Returns a newly allocated TkButton structure.
+ *
+ * Side effects:
+ * Registers an event handler for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkButton *
+TkpCreateButton(tkwin)
+ Tk_Window tkwin;
+{
+ WinButton *butPtr;
+
+ if (!initialized) {
+ UpdateButtonDefaults();
+ initialized = 1;
+ }
+
+ butPtr = (WinButton *)ckalloc(sizeof(WinButton));
+ butPtr->hwnd = NULL;
+ return (TkButton *) butPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateProc --
+ *
+ * This function creates a new Button control, subclasses
+ * the instance, and generates a new Window object.
+ *
+ * Results:
+ * Returns the newly allocated Window object, or None on failure.
+ *
+ * Side effects:
+ * Causes a new Button control to come into existence.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Window
+CreateProc(tkwin, parentWin, instanceData)
+ Tk_Window tkwin; /* Token for window. */
+ Window parentWin; /* Parent of new window. */
+ ClientData instanceData; /* Button instance data. */
+{
+ Window window;
+ HWND parent;
+ char *class;
+ WinButton *butPtr = (WinButton *)instanceData;
+
+ parent = Tk_GetHWND(parentWin);
+ if (butPtr->info.type == TYPE_LABEL) {
+ class = "STATIC";
+ butPtr->style = SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
+ } else {
+ class = "BUTTON";
+ butPtr->style = BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
+ }
+ butPtr->hwnd = CreateWindow(class, NULL, butPtr->style,
+ Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
+ parent, NULL, Tk_GetHINSTANCE(), NULL);
+ SetWindowPos(butPtr->hwnd, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ butPtr->oldProc = (WNDPROC)SetWindowLong(butPtr->hwnd, GWL_WNDPROC,
+ (DWORD) ButtonProc);
+
+ window = Tk_AttachHWND(tkwin, butPtr->hwnd);
+ return window;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyButton --
+ *
+ * Free data structures associated with the button control.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Restores the default control state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyButton(butPtr)
+ TkButton *butPtr;
+{
+ WinButton *winButPtr = (WinButton *)butPtr;
+ HWND hwnd = winButPtr->hwnd;
+ if (hwnd) {
+ SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winButPtr->oldProc);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayButton --
+ *
+ * This procedure is invoked to display a button widget. It is
+ * normally invoked as an idle handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen. The REDRAW_PENDING flag
+ * is cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ TkWinDCState state;
+ HDC dc;
+ register TkButton *butPtr = (TkButton *) clientData;
+ GC gc;
+ Tk_3DBorder border;
+ Pixmap pixmap;
+ int x = 0; /* Initialization only needed to stop
+ * compiler warning. */
+ int y, relief;
+ register Tk_Window tkwin = butPtr->tkwin;
+ int width, height;
+ int defaultWidth; /* Width of default ring. */
+ int offset; /* 0 means this is a label widget. 1 means
+ * it is a flavor of button, so we offset
+ * the text to make the button appear to
+ * move up and down as the relief changes. */
+
+ butPtr->flags &= ~REDRAW_PENDING;
+ if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+
+ border = butPtr->normalBorder;
+ if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
+ gc = butPtr->disabledGC;
+ } else if ((butPtr->state == tkActiveUid)
+ && !Tk_StrictMotif(butPtr->tkwin)) {
+ gc = butPtr->activeTextGC;
+ border = butPtr->activeBorder;
+ } else {
+ gc = butPtr->normalTextGC;
+ }
+ if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
+ && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
+ border = butPtr->selectBorder;
+ }
+
+ /*
+ * Override the relief specified for the button if this is a
+ * checkbutton or radiobutton and there's no indicator.
+ */
+
+ relief = butPtr->relief;
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
+ relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
+ : TK_RELIEF_RAISED;
+ }
+
+ /*
+ * Compute width of default ring and offset for pushed buttons.
+ */
+
+ if (butPtr->type == TYPE_BUTTON) {
+ defaultWidth = ((butPtr->defaultState == tkActiveUid)
+ ? butPtr->highlightWidth : 0);
+ offset = 1;
+ } else {
+ defaultWidth = 0;
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
+ offset = 1;
+ } else {
+ offset = 0;
+ }
+ }
+
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the button in a pixmap, then copies the pixmap to the
+ * screen in a single operation. This means that there's no
+ * point in time where the on-sreen image has been cleared.
+ */
+
+ pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+ Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
+ Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
+
+ /*
+ * Display image or bitmap or text for button.
+ */
+
+ if (butPtr->image != None) {
+ Tk_SizeOfImage(butPtr->image, &width, &height);
+
+ imageOrBitmap:
+ TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
+ butPtr->indicatorSpace + width, height, &x, &y);
+ x += butPtr->indicatorSpace;
+
+ if (relief == TK_RELIEF_SUNKEN) {
+ x += offset;
+ y += offset;
+ }
+ if (butPtr->image != NULL) {
+ if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
+ Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
+ pixmap, x, y);
+ } else {
+ Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
+ x, y);
+ }
+ } else {
+ XSetClipOrigin(butPtr->display, gc, x, y);
+ XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
+ (unsigned int) width, (unsigned int) height, x, y, 1);
+ XSetClipOrigin(butPtr->display, gc, 0, 0);
+ }
+ y += height/2;
+ } else if (butPtr->bitmap != None) {
+ Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ RECT rect;
+ TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
+ butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight,
+ &x, &y);
+
+ x += butPtr->indicatorSpace;
+
+ if (relief == TK_RELIEF_SUNKEN) {
+ x += offset;
+ y += offset;
+ }
+ Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
+ x, y, 0, -1);
+ Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
+ butPtr->textLayout, x, y, butPtr->underline);
+
+ /*
+ * Draw the focus ring. If this is a push button then we need to put
+ * it around the inner edge of the border, otherwise we put it around
+ * the text.
+ */
+
+ if (butPtr->flags & GOT_FOCUS && butPtr->type != TYPE_LABEL) {
+ dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
+ if (butPtr->type == TYPE_BUTTON || !butPtr->indicatorOn) {
+ rect.top = butPtr->borderWidth + 1 + defaultWidth;
+ rect.left = rect.top;
+ rect.right = Tk_Width(tkwin) - rect.left;
+ rect.bottom = Tk_Height(tkwin) - rect.top;
+ } else {
+ rect.top = y-2;
+ rect.left = x-2;
+ rect.right = x+butPtr->textWidth + 1;
+ rect.bottom = y+butPtr->textHeight + 1;
+ }
+ SetTextColor(dc, gc->foreground);
+ SetBkColor(dc, gc->background);
+ DrawFocusRect(dc, &rect);
+ TkWinReleaseDrawableDC(pixmap, dc, &state);
+ }
+ y += butPtr->textHeight/2;
+ }
+
+ /*
+ * Draw the indicator for check buttons and radio buttons. At this
+ * point x and y refer to the top-left corner of the text or image
+ * or bitmap.
+ */
+
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn
+ && boxesPtr) {
+ int xSrc, ySrc;
+
+ x -= butPtr->indicatorSpace;
+ y -= butPtr->indicatorDiameter / 2;
+
+ xSrc = (butPtr->flags & SELECTED) ? boxWidth : 0;
+ if (butPtr->state == tkActiveUid) {
+ xSrc += boxWidth*2;
+ }
+ ySrc = (butPtr->type == TYPE_RADIO_BUTTON) ? 0 : boxHeight;
+
+ /*
+ * Update the palette in the boxes bitmap to reflect the current
+ * button colors. Note that this code relies on the layout of the
+ * bitmap's palette. Also, all of the colors used to draw the
+ * bitmap must be in the palette that is selected into the DC of
+ * the offscreen pixmap. This requires that the static colors
+ * be placed into the palette.
+ */
+
+ boxesPalette[PAL_CHECK] = FlipColor(gc->foreground);
+ boxesPalette[PAL_TOP_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_DARK_GC));
+ boxesPalette[PAL_TOP_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_DARK2));
+ boxesPalette[PAL_BOTTOM_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_LIGHT2));
+ boxesPalette[PAL_BOTTOM_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_LIGHT_GC));
+ if (butPtr->state == tkDisabledUid) {
+ boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_LIGHT2));
+ } else if (butPtr->selectBorder != NULL) {
+ boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
+ butPtr->selectBorder, TK_3D_FLAT_GC));
+ } else {
+ boxesPalette[PAL_INTERIOR] = FlipColor(GetSysColor(COLOR_WINDOW));
+ }
+ boxesPalette[PAL_BACKGROUND] = FlipColor(TkWinGetBorderPixels(tkwin,
+ border, TK_3D_FLAT_GC));
+
+ dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
+ StretchDIBits(dc, x, y, boxWidth, boxHeight, xSrc, ySrc,
+ boxWidth, boxHeight, boxesBits, (LPBITMAPINFO)boxesPtr,
+ DIB_RGB_COLORS, SRCCOPY);
+ TkWinReleaseDrawableDC(pixmap, dc, &state);
+ }
+
+ /*
+ * If the button is disabled with a stipple rather than a special
+ * foreground color, generate the stippled effect. If the widget
+ * is selected and we use a different background color when selected,
+ * must temporarily modify the GC.
+ */
+
+ if ((butPtr->state == tkDisabledUid)
+ && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
+ if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
+ && (butPtr->selectBorder != NULL)) {
+ XSetForeground(butPtr->display, butPtr->disabledGC,
+ Tk_3DBorderColor(butPtr->selectBorder)->pixel);
+ }
+ XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
+ butPtr->inset, butPtr->inset,
+ (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
+ (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
+ if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
+ && (butPtr->selectBorder != NULL)) {
+ XSetForeground(butPtr->display, butPtr->disabledGC,
+ Tk_3DBorderColor(butPtr->normalBorder)->pixel);
+ }
+ }
+
+ /*
+ * Draw the border and traversal highlight last. This way, if the
+ * button's contents overflow they'll be covered up by the border.
+ */
+
+ if (relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tkwin, pixmap, border,
+ defaultWidth, defaultWidth,
+ Tk_Width(tkwin) - 2*defaultWidth,
+ Tk_Height(tkwin) - 2*defaultWidth,
+ butPtr->borderWidth, relief);
+ }
+ if (defaultWidth != 0) {
+ dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
+ TkWinFillRect(dc, 0, 0, Tk_Width(tkwin), defaultWidth,
+ butPtr->highlightColorPtr->pixel);
+ TkWinFillRect(dc, 0, 0, defaultWidth, Tk_Height(tkwin),
+ butPtr->highlightColorPtr->pixel);
+ TkWinFillRect(dc, 0, Tk_Height(tkwin) - defaultWidth,
+ Tk_Width(tkwin), defaultWidth,
+ butPtr->highlightColorPtr->pixel);
+ TkWinFillRect(dc, Tk_Width(tkwin) - defaultWidth, 0,
+ defaultWidth, Tk_Height(tkwin),
+ butPtr->highlightColorPtr->pixel);
+ TkWinReleaseDrawableDC(pixmap, dc, &state);
+ }
+
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
+ butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
+ (unsigned) Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(butPtr->display, pixmap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpComputeButtonGeometry --
+ *
+ * After changes in a button's text or bitmap, this procedure
+ * recomputes the button's geometry and passes this information
+ * along to the geometry manager for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The button's window may change size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpComputeButtonGeometry(butPtr)
+ register TkButton *butPtr; /* Button whose geometry may have changed. */
+{
+ int width, height, avgWidth;
+ Tk_FontMetrics fm;
+
+ if (butPtr->highlightWidth < 0) {
+ butPtr->highlightWidth = 0;
+ }
+ butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
+ butPtr->indicatorSpace = 0;
+
+ if (!boxesPtr) {
+ InitBoxes();
+ }
+
+ if (butPtr->image != NULL) {
+ Tk_SizeOfImage(butPtr->image, &width, &height);
+ imageOrBitmap:
+ if (butPtr->width > 0) {
+ width = butPtr->width;
+ }
+ if (butPtr->height > 0) {
+ height = butPtr->height;
+ }
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
+ butPtr->indicatorSpace = boxWidth * 2;
+ butPtr->indicatorDiameter = boxHeight;
+ }
+ } else if (butPtr->bitmap != None) {
+ Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ Tk_FreeTextLayout(butPtr->textLayout);
+ butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
+ butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
+ &butPtr->textWidth, &butPtr->textHeight);
+
+ width = butPtr->textWidth;
+ height = butPtr->textHeight;
+ avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
+ Tk_GetFontMetrics(butPtr->tkfont, &fm);
+
+ if (butPtr->width > 0) {
+ width = butPtr->width * avgWidth;
+ }
+ if (butPtr->height > 0) {
+ height = butPtr->height * fm.linespace;
+ }
+
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
+ butPtr->indicatorDiameter = boxHeight;
+ butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
+ }
+
+ /*
+ * Increase the inset to allow for the focus ring.
+ */
+
+ if (butPtr->type != TYPE_LABEL) {
+ butPtr->inset += 3;
+ }
+ }
+
+ /*
+ * When issuing the geometry request, add extra space for the indicator,
+ * if any, and for the border and padding, plus an extra pixel so the
+ * display can be offset by 1 pixel in either direction for the raised
+ * or lowered effect.
+ */
+
+ if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
+ width += 2*butPtr->padX;
+ height += 2*butPtr->padY;
+ }
+ if ((butPtr->type == TYPE_BUTTON)
+ || ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn)) {
+ width += 1;
+ height += 1;
+ }
+ Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
+ + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
+ Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ButtonProc --
+ *
+ * This function is call by Windows whenever an event occurs on
+ * a button control created by Tk.
+ *
+ * Results:
+ * Standard Windows return value.
+ *
+ * Side effects:
+ * May generate events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static LRESULT CALLBACK
+ButtonProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ LRESULT result;
+ WinButton *butPtr;
+ Tk_Window tkwin = Tk_HWNDToWindow(hwnd);
+
+ if (tkwin == NULL) {
+ panic("ButtonProc called on an invalid HWND");
+ }
+ butPtr = (WinButton *)((TkWindow*)tkwin)->instanceData;
+
+ switch(message) {
+ case WM_ERASEBKGND:
+ return 0;
+
+ case BM_GETCHECK:
+ if (((butPtr->info.type == TYPE_CHECK_BUTTON)
+ || (butPtr->info.type == TYPE_RADIO_BUTTON))
+ && butPtr->info.indicatorOn) {
+ return (butPtr->info.flags & SELECTED)
+ ? BST_CHECKED : BST_UNCHECKED;
+ }
+ return 0;
+
+ case BM_GETSTATE: {
+ DWORD state = 0;
+ if (((butPtr->info.type == TYPE_CHECK_BUTTON)
+ || (butPtr->info.type == TYPE_RADIO_BUTTON))
+ && butPtr->info.indicatorOn) {
+ state = (butPtr->info.flags & SELECTED)
+ ? BST_CHECKED : BST_UNCHECKED;
+ }
+ if (butPtr->info.flags & GOT_FOCUS) {
+ state |= BST_FOCUS;
+ }
+ return state;
+ }
+ case WM_ENABLE:
+ break;
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ EndPaint(hwnd, &ps);
+ TkpDisplayButton((ClientData)butPtr);
+ return 0;
+ }
+ case BN_CLICKED: {
+ int code;
+ Tcl_Interp *interp = butPtr->info.interp;
+ if (butPtr->info.state != tkDisabledUid) {
+ Tcl_Preserve((ClientData)interp);
+ code = TkInvokeButton((TkButton*)butPtr);
+ if (code != TCL_OK && code != TCL_CONTINUE
+ && code != TCL_BREAK) {
+ Tcl_AddErrorInfo(interp, "\n (button invoke)");
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_Release((ClientData)interp);
+ }
+ Tcl_ServiceAll();
+ return 0;
+ }
+
+ default:
+ if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) {
+ return result;
+ }
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
diff --git a/win/tkWinClipboard.c b/win/tkWinClipboard.c
new file mode 100644
index 0000000..9d4237a
--- /dev/null
+++ b/win/tkWinClipboard.c
@@ -0,0 +1,291 @@
+/*
+ * tkWinClipboard.c --
+ *
+ * This file contains functions for managing the clipboard.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinClipboard.c 1.8 97/05/20 17:01:13
+ */
+
+#include "tkWinInt.h"
+#include "tkSelect.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSelGetSelection --
+ *
+ * Retrieve the specified selection from another process. For
+ * now, only fetching XA_STRING from CLIPBOARD is supported.
+ * Eventually other types should be allowed.
+ *
+ * Results:
+ * The return value is a standard Tcl return value.
+ * If an error occurs (such as no selection exists)
+ * then an error message is left in interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
+ Tcl_Interp *interp; /* Interpreter to use for reporting
+ * errors. */
+ Tk_Window tkwin; /* Window on whose behalf to retrieve
+ * the selection (determines display
+ * from which to retrieve). */
+ Atom selection; /* Selection to retrieve. */
+ Atom target; /* Desired form in which selection
+ * is to be returned. */
+ Tk_GetSelProc *proc; /* Procedure to call to process the
+ * selection, once it has been retrieved. */
+ ClientData clientData; /* Arbitrary value to pass to proc. */
+{
+ char *data, *buffer, *destPtr;
+ HGLOBAL handle;
+ int result, length;
+
+ if ((selection == Tk_InternAtom(tkwin, "CLIPBOARD"))
+ && (target == XA_STRING)) {
+ if (OpenClipboard(NULL)) {
+ handle = GetClipboardData(CF_TEXT);
+ if (handle != NULL) {
+ data = GlobalLock(handle);
+ length = strlen(data);
+ buffer = ckalloc(length+1);
+ destPtr = buffer;
+ while (*data != '\0') {
+ if (*data != '\r') {
+ *destPtr = *data;
+ destPtr++;
+ }
+ data++;
+ }
+ *destPtr = '\0';
+ GlobalUnlock(handle);
+ CloseClipboard();
+ result = (*proc)(clientData, interp, buffer);
+ ckfree(buffer);
+ return result;
+ }
+ CloseClipboard();
+ }
+ }
+
+ Tcl_AppendResult(interp, Tk_GetAtomName(tkwin, selection),
+ " selection doesn't exist or form \"", Tk_GetAtomName(tkwin, target),
+ "\" not defined", (char *) NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSetSelectionOwner --
+ *
+ * This function claims ownership of the specified selection.
+ * If the selection is CLIPBOARD, then we empty the system
+ * clipboard.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Empties the system clipboard, and claims ownership.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XSetSelectionOwner(display, selection, owner, time)
+ Display* display;
+ Atom selection;
+ Window owner;
+ Time time;
+{
+ HWND hwnd = owner ? TkWinGetHWND(owner) : NULL;
+ Tk_Window tkwin;
+
+ /*
+ * This is a gross hack because the Tk_InternAtom interface is broken.
+ * It expects a Tk_Window, even though it only needs a Tk_Display.
+ */
+
+ tkwin = (Tk_Window)tkMainWindowList->winPtr;
+
+ if (selection == Tk_InternAtom(tkwin, "CLIPBOARD")) {
+
+ /*
+ * Only claim and empty the clipboard if we aren't already the
+ * owner of the clipboard.
+ */
+
+ if (GetClipboardOwner() != hwnd) {
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, NULL);
+ CloseClipboard();
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinClipboardRender --
+ *
+ * This function supplies the contents of the clipboard in
+ * response to a WM_RENDERFORMAT message.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets the contents of the clipboard.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinClipboardRender(dispPtr, format)
+ TkDisplay *dispPtr;
+ UINT format;
+{
+ TkClipboardTarget *targetPtr;
+ TkClipboardBuffer *cbPtr;
+ HGLOBAL handle;
+ char *buffer, *p, *endPtr;
+ int length;
+
+ for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL;
+ targetPtr = targetPtr->nextPtr) {
+ if (targetPtr->type == XA_STRING)
+ break;
+ }
+ length = 0;
+ if (targetPtr != NULL) {
+ for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL;
+ cbPtr = cbPtr->nextPtr) {
+ length += cbPtr->length;
+ for (p = cbPtr->buffer, endPtr = p + cbPtr->length;
+ p < endPtr; p++) {
+ if (*p == '\n') {
+ length++;
+ }
+ }
+ }
+ }
+ handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, length+1);
+ if (!handle) {
+ return;
+ }
+ buffer = GlobalLock(handle);
+ if (targetPtr != NULL) {
+ for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL;
+ cbPtr = cbPtr->nextPtr) {
+ for (p = cbPtr->buffer, endPtr = p + cbPtr->length;
+ p < endPtr; p++) {
+ if (*p == '\n') {
+ *buffer++ = '\r';
+ }
+ *buffer++ = *p;
+ }
+ }
+ }
+ *buffer = '\0';
+ GlobalUnlock(handle);
+ SetClipboardData(CF_TEXT, handle);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSelUpdateClipboard --
+ *
+ * This function is called to force the clipboard to be updated
+ * after new data is added.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Clears the current contents of the clipboard.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkSelUpdateClipboard(winPtr, targetPtr)
+ TkWindow *winPtr;
+ TkClipboardTarget *targetPtr;
+{
+ HWND hwnd = TkWinGetHWND(winPtr->window);
+
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, NULL);
+ CloseClipboard();
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkSelEventProc --
+ *
+ * This procedure is invoked whenever a selection-related
+ * event occurs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Lots: depends on the type of event.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkSelEventProc(tkwin, eventPtr)
+ Tk_Window tkwin; /* Window for which event was
+ * targeted. */
+ register XEvent *eventPtr; /* X event: either SelectionClear,
+ * SelectionRequest, or
+ * SelectionNotify. */
+{
+ if (eventPtr->type == SelectionClear) {
+ TkSelClearSelection(tkwin, eventPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSelPropProc --
+ *
+ * This procedure is invoked when property-change events
+ * occur on windows not known to the toolkit. This is a stub
+ * function under Windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkSelPropProc(eventPtr)
+ register XEvent *eventPtr; /* X PropertyChange event. */
+{
+}
diff --git a/win/tkWinColor.c b/win/tkWinColor.c
new file mode 100644
index 0000000..2cc3d09
--- /dev/null
+++ b/win/tkWinColor.c
@@ -0,0 +1,615 @@
+/*
+ * tkWinColor.c --
+ *
+ * Functions to map color names to system color values.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinColor.c 1.20 97/10/27 16:39:23
+ */
+
+#include <tkColor.h>
+#include <tkWinInt.h>
+
+/*
+ * The following structure is used to keep track of each color that is
+ * allocated by this module.
+ */
+
+typedef struct WinColor {
+ TkColor info; /* Generic color information. */
+ int index; /* Index for GetSysColor(), -1 if color
+ * is not a "live" system color. */
+} WinColor;
+
+/*
+ * colorTable is a hash table used to look up X colors by name.
+ */
+
+static Tcl_HashTable colorTable;
+
+/*
+ * The sysColors array contains the names and index values for the
+ * Windows indirect system color names. In use, all of the names
+ * will have the string "System" prepended, but we omit it in the table
+ * to save space.
+ */
+
+typedef struct {
+ char *name;
+ int index;
+} SystemColorEntry;
+
+
+static SystemColorEntry sysColors[] = {
+ "3dDarkShadow", COLOR_3DDKSHADOW,
+ "3dLight", COLOR_3DLIGHT,
+ "ActiveBorder", COLOR_ACTIVEBORDER,
+ "ActiveCaption", COLOR_ACTIVECAPTION,
+ "AppWorkspace", COLOR_APPWORKSPACE,
+ "Background", COLOR_BACKGROUND,
+ "ButtonFace", COLOR_BTNFACE,
+ "ButtonHighlight", COLOR_BTNHIGHLIGHT,
+ "ButtonShadow", COLOR_BTNSHADOW,
+ "ButtonText", COLOR_BTNTEXT,
+ "CaptionText", COLOR_CAPTIONTEXT,
+ "DisabledText", COLOR_GRAYTEXT,
+ "GrayText", COLOR_GRAYTEXT,
+ "Highlight", COLOR_HIGHLIGHT,
+ "HighlightText", COLOR_HIGHLIGHTTEXT,
+ "InactiveBorder", COLOR_INACTIVEBORDER,
+ "InactiveCaption", COLOR_INACTIVECAPTION,
+ "InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT,
+ "InfoBackground", COLOR_INFOBK,
+ "InfoText", COLOR_INFOTEXT,
+ "Menu", COLOR_MENU,
+ "MenuText", COLOR_MENUTEXT,
+ "Scrollbar", COLOR_SCROLLBAR,
+ "Window", COLOR_WINDOW,
+ "WindowFrame", COLOR_WINDOWFRAME,
+ "WindowText", COLOR_WINDOWTEXT,
+ NULL, 0
+};
+
+static int ncolors = 0;
+
+/*
+ * Forward declarations for functions defined later in this file.
+ */
+
+static int FindSystemColor _ANSI_ARGS_((const char *name,
+ XColor *colorPtr, int *indexPtr));
+static int GetColorByName _ANSI_ARGS_((char *name, XColor *color));
+static int GetColorByValue _ANSI_ARGS_((char *value, XColor *color));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindSystemColor --
+ *
+ * This routine finds the color entry that corresponds to the
+ * specified color.
+ *
+ * Results:
+ * Returns non-zero on success. The RGB values of the XColor
+ * will be initialized to the proper values on success.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+FindSystemColor(name, colorPtr, indexPtr)
+ const char *name; /* Color name. */
+ XColor *colorPtr; /* Where to store results. */
+ int *indexPtr; /* Out parameter to store color index. */
+{
+ int l, u, r, i;
+
+ /*
+ * Count the number of elements in the color array if we haven't
+ * done so yet.
+ */
+
+ if (ncolors == 0) {
+ SystemColorEntry *ePtr;
+ int version;
+
+ version = LOBYTE(LOWORD(GetVersion()));
+ for (ePtr = sysColors; ePtr->name != NULL; ePtr++) {
+ if (version < 4) {
+ if (ePtr->index == COLOR_3DDKSHADOW) {
+ ePtr->index = COLOR_BTNSHADOW;
+ } else if (ePtr->index == COLOR_3DLIGHT) {
+ ePtr->index = COLOR_BTNHIGHLIGHT;
+ }
+ }
+ ncolors++;
+ }
+ }
+
+ /*
+ * Perform a binary search on the sorted array of colors.
+ */
+
+ l = 0;
+ u = ncolors - 1;
+ while (l <= u) {
+ i = (l + u) / 2;
+ r = strcasecmp(name, sysColors[i].name);
+ if (r == 0) {
+ break;
+ } else if (r < 0) {
+ u = i-1;
+ } else {
+ l = i+1;
+ }
+ }
+ if (l > u) {
+ return 0;
+ }
+
+ *indexPtr = sysColors[i].index;
+ colorPtr->pixel = GetSysColor(sysColors[i].index);
+ colorPtr->red = GetRValue(colorPtr->pixel) << 8;
+ colorPtr->green = GetGValue(colorPtr->pixel) << 8;
+ colorPtr->blue = GetBValue(colorPtr->pixel) << 8;
+ colorPtr->flags = DoRed|DoGreen|DoBlue;
+ colorPtr->pad = 0;
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetColor --
+ *
+ * Allocate a new TkColor for the color with the given name.
+ *
+ * Results:
+ * Returns a newly allocated TkColor, or NULL on failure.
+ *
+ * Side effects:
+ * May invalidate the colormap cache associated with tkwin upon
+ * allocating a new colormap entry. Allocates a new TkColor
+ * structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkColor *
+TkpGetColor(tkwin, name)
+ Tk_Window tkwin; /* Window in which color will be used. */
+ Tk_Uid name; /* Name of color to allocated (in form
+ * suitable for passing to XParseColor). */
+{
+ WinColor *winColPtr;
+ XColor color;
+ int index = -1; /* -1 indicates that this is not an indirect
+ * sytem color. */
+
+ /*
+ * Check to see if it is a system color or an X color string. If the
+ * color is found, allocate a new WinColor and store the XColor and the
+ * system color index.
+ */
+
+ if (((strncasecmp(name, "system", 6) == 0)
+ && FindSystemColor(name+6, &color, &index))
+ || XParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), name,
+ &color)) {
+ winColPtr = (WinColor *) ckalloc(sizeof(WinColor));
+ winColPtr->info.color = color;
+ winColPtr->index = index;
+
+ XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin),
+ &winColPtr->info.color);
+ return (TkColor *) winColPtr;
+ }
+ return (TkColor *) NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetColorByValue --
+ *
+ * Given a desired set of red-green-blue intensities for a color,
+ * locate a pixel value to use to draw that color in a given
+ * window.
+ *
+ * Results:
+ * The return value is a pointer to an TkColor structure that
+ * indicates the closest red, blue, and green intensities available
+ * to those specified in colorPtr, and also specifies a pixel
+ * value to use to draw in that color.
+ *
+ * Side effects:
+ * May invalidate the colormap cache for the specified window.
+ * Allocates a new TkColor structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkColor *
+TkpGetColorByValue(tkwin, colorPtr)
+ Tk_Window tkwin; /* Window in which color will be used. */
+ XColor *colorPtr; /* Red, green, and blue fields indicate
+ * desired color. */
+{
+ WinColor *tkColPtr = (WinColor *) ckalloc(sizeof(WinColor));
+
+ tkColPtr->info.color.red = colorPtr->red;
+ tkColPtr->info.color.green = colorPtr->green;
+ tkColPtr->info.color.blue = colorPtr->blue;
+ tkColPtr->info.color.pixel = 0;
+ tkColPtr->index = -1;
+ XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin), &tkColPtr->info.color);
+ return (TkColor *) tkColPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpFreeColor --
+ *
+ * Release the specified color back to the system.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Invalidates the colormap cache for the colormap associated with
+ * the given color.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpFreeColor(tkColPtr)
+ TkColor *tkColPtr; /* Color to be released. Must have been
+ * allocated by TkpGetColor or
+ * TkpGetColorByValue. */
+{
+ Screen *screen = tkColPtr->screen;
+
+ XFreeColors(DisplayOfScreen(screen), tkColPtr->colormap,
+ &tkColPtr->color.pixel, 1, 0L);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinIndexOfColor --
+ *
+ * Given a color, return the system color index that was used
+ * to create the color.
+ *
+ * Results:
+ * If the color was allocated using a system indirect color name,
+ * then the corresponding GetSysColor() index is returned.
+ * Otherwise, -1 is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkWinIndexOfColor(colorPtr)
+ XColor *colorPtr;
+{
+ register WinColor *winColPtr = (WinColor *) colorPtr;
+ if (winColPtr->info.magic == COLOR_MAGIC) {
+ return winColPtr->index;
+ }
+ return -1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XAllocColor --
+ *
+ * Find the closest available color to the specified XColor.
+ *
+ * Results:
+ * Updates the color argument and returns 1 on success. Otherwise
+ * returns 0.
+ *
+ * Side effects:
+ * Allocates a new color in the palette.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XAllocColor(display, colormap, color)
+ Display* display;
+ Colormap colormap;
+ XColor* color;
+{
+ TkWinColormap *cmap = (TkWinColormap *) colormap;
+ PALETTEENTRY entry, closeEntry;
+ HDC dc = GetDC(NULL);
+
+ entry.peRed = (color->red) >> 8;
+ entry.peGreen = (color->green) >> 8;
+ entry.peBlue = (color->blue) >> 8;
+ entry.peFlags = 0;
+
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) {
+ unsigned long sizePalette = GetDeviceCaps(dc, SIZEPALETTE);
+ UINT newPixel, closePixel;
+ int new, refCount;
+ Tcl_HashEntry *entryPtr;
+ UINT index;
+
+ /*
+ * Find the nearest existing palette entry.
+ */
+
+ newPixel = RGB(entry.peRed, entry.peGreen, entry.peBlue);
+ index = GetNearestPaletteIndex(cmap->palette, newPixel);
+ GetPaletteEntries(cmap->palette, index, 1, &closeEntry);
+ closePixel = RGB(closeEntry.peRed, closeEntry.peGreen,
+ closeEntry.peBlue);
+
+ /*
+ * If this is not a duplicate, allocate a new entry. Note that
+ * we may get values for index that are above the current size
+ * of the palette. This happens because we don't shrink the size of
+ * the palette object when we deallocate colors so there may be
+ * stale values that match in the upper slots. We should ignore
+ * those values and just put the new color in as if the colors
+ * had not matched.
+ */
+
+ if ((index >= cmap->size) || (newPixel != closePixel)) {
+ if (cmap->size == sizePalette) {
+ color->red = closeEntry.peRed << 8;
+ color->green = closeEntry.peGreen << 8;
+ color->blue = closeEntry.peBlue << 8;
+ entry = closeEntry;
+ if (index >= cmap->size) {
+ OutputDebugString("XAllocColor: Colormap is bigger than we thought");
+ }
+ } else {
+ cmap->size++;
+ ResizePalette(cmap->palette, cmap->size);
+ SetPaletteEntries(cmap->palette, cmap->size - 1, 1, &entry);
+ }
+ }
+
+ color->pixel = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue);
+ entryPtr = Tcl_CreateHashEntry(&cmap->refCounts,
+ (char *) color->pixel, &new);
+ if (new) {
+ refCount = 1;
+ } else {
+ refCount = ((int) Tcl_GetHashValue(entryPtr)) + 1;
+ }
+ Tcl_SetHashValue(entryPtr, (ClientData)refCount);
+ } else {
+
+ /*
+ * Determine what color will actually be used on non-colormap systems.
+ */
+
+ color->pixel = GetNearestColor(dc,
+ RGB(entry.peRed, entry.peGreen, entry.peBlue));
+ color->red = (GetRValue(color->pixel) << 8);
+ color->green = (GetGValue(color->pixel) << 8);
+ color->blue = (GetBValue(color->pixel) << 8);
+ }
+
+ ReleaseDC(NULL, dc);
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFreeColors --
+ *
+ * Deallocate a block of colors.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Removes entries for the current palette and compacts the
+ * remaining set.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFreeColors(display, colormap, pixels, npixels, planes)
+ Display* display;
+ Colormap colormap;
+ unsigned long* pixels;
+ int npixels;
+ unsigned long planes;
+{
+ TkWinColormap *cmap = (TkWinColormap *) colormap;
+ COLORREF cref;
+ UINT count, index, refCount;
+ int i;
+ PALETTEENTRY entry, *entries;
+ Tcl_HashEntry *entryPtr;
+ HDC dc = GetDC(NULL);
+
+ /*
+ * We don't have to do anything for non-palette devices.
+ */
+
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) {
+
+ /*
+ * This is really slow for large values of npixels.
+ */
+
+ for (i = 0; i < npixels; i++) {
+ entryPtr = Tcl_FindHashEntry(&cmap->refCounts,
+ (char *) pixels[i]);
+ if (!entryPtr) {
+ panic("Tried to free a color that isn't allocated.");
+ }
+ refCount = (int) Tcl_GetHashValue(entryPtr) - 1;
+ if (refCount == 0) {
+ cref = pixels[i] & 0x00ffffff;
+ index = GetNearestPaletteIndex(cmap->palette, cref);
+ GetPaletteEntries(cmap->palette, index, 1, &entry);
+ if (cref == RGB(entry.peRed, entry.peGreen, entry.peBlue)) {
+ count = cmap->size - index;
+ entries = (PALETTEENTRY *) ckalloc(sizeof(PALETTEENTRY)
+ * count);
+ GetPaletteEntries(cmap->palette, index+1, count, entries);
+ SetPaletteEntries(cmap->palette, index, count, entries);
+ ckfree((char *) entries);
+ cmap->size--;
+ } else {
+ panic("Tried to free a color that isn't allocated.");
+ }
+ Tcl_DeleteHashEntry(entryPtr);
+ } else {
+ Tcl_SetHashValue(entryPtr, (ClientData)refCount);
+ }
+ }
+ }
+ ReleaseDC(NULL, dc);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XCreateColormap --
+ *
+ * Allocate a new colormap.
+ *
+ * Results:
+ * Returns a newly allocated colormap.
+ *
+ * Side effects:
+ * Allocates an empty palette and color list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Colormap
+XCreateColormap(display, w, visual, alloc)
+ Display* display;
+ Window w;
+ Visual* visual;
+ int alloc;
+{
+ char logPalBuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
+ LOGPALETTE *logPalettePtr;
+ PALETTEENTRY *entryPtr;
+ TkWinColormap *cmap;
+ Tcl_HashEntry *hashPtr;
+ int new;
+ UINT i;
+ HPALETTE sysPal;
+
+ /*
+ * Allocate a starting palette with all of the reserved colors.
+ */
+
+ logPalettePtr = (LOGPALETTE *) logPalBuf;
+ logPalettePtr->palVersion = 0x300;
+ sysPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
+ logPalettePtr->palNumEntries = GetPaletteEntries(sysPal, 0, 256,
+ logPalettePtr->palPalEntry);
+
+ cmap = (TkWinColormap *) ckalloc(sizeof(TkWinColormap));
+ cmap->size = logPalettePtr->palNumEntries;
+ cmap->stale = 0;
+ cmap->palette = CreatePalette(logPalettePtr);
+
+ /*
+ * Add hash entries for each of the static colors.
+ */
+
+ Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS);
+ for (i = 0; i < logPalettePtr->palNumEntries; i++) {
+ entryPtr = logPalettePtr->palPalEntry + i;
+ hashPtr = Tcl_CreateHashEntry(&cmap->refCounts, (char*) PALETTERGB(
+ entryPtr->peRed, entryPtr->peGreen, entryPtr->peBlue), &new);
+ Tcl_SetHashValue(hashPtr, (ClientData)1);
+ }
+
+ return (Colormap)cmap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFreeColormap --
+ *
+ * Frees the resources associated with the given colormap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Deletes the palette associated with the colormap. Note that
+ * the palette must not be selected into a device context when
+ * this occurs.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFreeColormap(display, colormap)
+ Display* display;
+ Colormap colormap;
+{
+ TkWinColormap *cmap = (TkWinColormap *) colormap;
+ if (!DeleteObject(cmap->palette)) {
+ panic("Unable to free colormap, palette is still selected.");
+ }
+ Tcl_DeleteHashTable(&cmap->refCounts);
+ ckfree((char *) cmap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinSelectPalette --
+ *
+ * This function sets up the specified device context with a
+ * given palette. If the palette is stale, it realizes it in
+ * the background unless the palette is the current global
+ * palette.
+ *
+ * Results:
+ * Returns the previous palette selected into the device context.
+ *
+ * Side effects:
+ * May change the system palette.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HPALETTE
+TkWinSelectPalette(dc, colormap)
+ HDC dc;
+ Colormap colormap;
+{
+ TkWinColormap *cmap = (TkWinColormap *) colormap;
+ HPALETTE oldPalette;
+
+ oldPalette = SelectPalette(dc, cmap->palette,
+ (cmap->palette == TkWinGetSystemPalette()) ? FALSE : TRUE);
+ RealizePalette(dc);
+ return oldPalette;
+}
diff --git a/win/tkWinCursor.c b/win/tkWinCursor.c
new file mode 100644
index 0000000..bf81d8f
--- /dev/null
+++ b/win/tkWinCursor.c
@@ -0,0 +1,210 @@
+/*
+ * tkWinCursor.c --
+ *
+ * This file contains Win32 specific cursor related routines.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinCursor.c 1.10 97/09/02 13:21:01
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * The following data structure contains the system specific data
+ * necessary to control Windows cursors.
+ */
+
+typedef struct {
+ TkCursor info; /* Generic cursor info used by tkCursor.c */
+ HCURSOR winCursor; /* Win32 cursor handle. */
+ int system; /* 1 if cursor is a system cursor, else 0. */
+} TkWinCursor;
+
+/*
+ * The table below is used to map from the name of a predefined cursor
+ * to its resource identifier.
+ */
+
+static struct CursorName {
+ char *name;
+ LPCTSTR id;
+} cursorNames[] = {
+ {"starting", IDC_APPSTARTING},
+ {"arrow", IDC_ARROW},
+ {"ibeam", IDC_IBEAM},
+ {"icon", IDC_ICON},
+ {"no", IDC_NO},
+ {"size", IDC_SIZE},
+ {"size_ne_sw", IDC_SIZENESW},
+ {"size_ns", IDC_SIZENS},
+ {"size_nw_se", IDC_SIZENWSE},
+ {"size_we", IDC_SIZEWE},
+ {"uparrow", IDC_UPARROW},
+ {"wait", IDC_WAIT},
+ {"crosshair", IDC_CROSS},
+ {"fleur", IDC_SIZE},
+ {"sb_v_double_arrow", IDC_SIZENS},
+ {"sb_h_double_arrow", IDC_SIZEWE},
+ {"center_ptr", IDC_UPARROW},
+ {"watch", IDC_WAIT},
+ {"xterm", IDC_IBEAM},
+ {NULL, 0}
+};
+
+/*
+ * The default cursor is used whenever no other cursor has been specified.
+ */
+
+#define TK_DEFAULT_CURSOR IDC_ARROW
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetCursorByName --
+ *
+ * Retrieve a system cursor by name.
+ *
+ * Results:
+ * Returns a new cursor, or NULL on errors.
+ *
+ * Side effects:
+ * Allocates a new cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkCursor *
+TkGetCursorByName(interp, tkwin, string)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ Tk_Window tkwin; /* Window in which cursor will be used. */
+ Tk_Uid string; /* Description of cursor. See manual entry
+ * for details on legal syntax. */
+{
+ struct CursorName *namePtr;
+ TkWinCursor *cursorPtr;
+
+ /*
+ * Check for the cursor in the system cursor set.
+ */
+
+ for (namePtr = cursorNames; namePtr->name != NULL; namePtr++) {
+ if (strcmp(namePtr->name, string) == 0) {
+ break;
+ }
+ }
+
+ cursorPtr = (TkWinCursor *) ckalloc(sizeof(TkWinCursor));
+ cursorPtr->info.cursor = (Tk_Cursor) cursorPtr;
+ cursorPtr->winCursor = NULL;
+ if (namePtr->name != NULL) {
+ cursorPtr->winCursor = LoadCursor(NULL, namePtr->id);
+ cursorPtr->system = 1;
+ }
+ if (cursorPtr->winCursor == NULL) {
+ cursorPtr->winCursor = LoadCursor(Tk_GetHINSTANCE(), string);
+ cursorPtr->system = 0;
+ }
+ if (cursorPtr->winCursor == NULL) {
+ ckfree((char *)cursorPtr);
+ Tcl_AppendResult(interp, "bad cursor spec \"", string, "\"",
+ (char *) NULL);
+ return NULL;
+ } else {
+ return (TkCursor *) cursorPtr;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCreateCursorFromData --
+ *
+ * Creates a cursor from the source and mask bits.
+ *
+ * Results:
+ * Returns a new cursor, or NULL on errors.
+ *
+ * Side effects:
+ * Allocates a new cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkCursor *
+TkCreateCursorFromData(tkwin, source, mask, width, height, xHot, yHot,
+ fgColor, bgColor)
+ Tk_Window tkwin; /* Window in which cursor will be used. */
+ char *source; /* Bitmap data for cursor shape. */
+ char *mask; /* Bitmap data for cursor mask. */
+ int width, height; /* Dimensions of cursor. */
+ int xHot, yHot; /* Location of hot-spot in cursor. */
+ XColor fgColor; /* Foreground color for cursor. */
+ XColor bgColor; /* Background color for cursor. */
+{
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkFreeCursor --
+ *
+ * This procedure is called to release a cursor allocated by
+ * TkGetCursorByName.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor data structure is deallocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkFreeCursor(cursorPtr)
+ TkCursor *cursorPtr;
+{
+ TkWinCursor *winCursorPtr = (TkWinCursor *) cursorPtr;
+ ckfree((char *) winCursorPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetCursor --
+ *
+ * Set the global cursor. If the cursor is None, then use the
+ * default Tk cursor.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the mouse cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetCursor(cursor)
+ TkpCursor cursor;
+{
+ HCURSOR hcursor;
+ TkWinCursor *winCursor = (TkWinCursor *) cursor;
+
+ if (winCursor == NULL || winCursor->winCursor == NULL) {
+ hcursor = LoadCursor(NULL, TK_DEFAULT_CURSOR);
+ } else {
+ hcursor = winCursor->winCursor;
+ }
+
+ if (hcursor != NULL) {
+ SetCursor(hcursor);
+ }
+}
diff --git a/win/tkWinDefault.h b/win/tkWinDefault.h
new file mode 100644
index 0000000..c82f3c8
--- /dev/null
+++ b/win/tkWinDefault.h
@@ -0,0 +1,456 @@
+/*
+ * tkWinDefault.h --
+ *
+ * This file defines the defaults for all options for all of
+ * the Tk widgets.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinDefault.h 1.34 97/10/09 17:45:20
+ */
+
+#ifndef _TKWINDEFAULT
+#define _TKWINDEFAULT
+
+/*
+ * The definitions below provide symbolic names for the default colors.
+ * NORMAL_BG - Normal background color.
+ * ACTIVE_BG - Background color when widget is active.
+ * SELECT_BG - Background color for selected text.
+ * TROUGH - Background color for troughs in scales and scrollbars.
+ * INDICATOR - Color for indicator when button is selected.
+ * DISABLED - Foreground color when widget is disabled.
+ */
+
+#define BLACK "Black"
+#define WHITE "White"
+
+#define CTL_FONT "{MS Sans Serif} 8"
+#define NORMAL_BG "SystemButtonFace"
+#define NORMAL_FG "SystemButtonText"
+#define ACTIVE_BG NORMAL_BG
+#define TEXT_FG "SystemWindowText"
+#define SELECT_BG "SystemHighlight"
+#define SELECT_FG "SystemHighlightText"
+#define TROUGH "SystemScrollbar"
+#define INDICATOR "SystemWindow"
+#define DISABLED "SystemDisabledText"
+#define MENU_BG "SystemMenu"
+#define MENU_FG "SystemMenuText"
+#define HIGHLIGHT "SystemWindowFrame"
+
+/*
+ * Defaults for labels, buttons, checkbuttons, and radiobuttons:
+ */
+
+#define DEF_BUTTON_ANCHOR "center"
+#define DEF_BUTTON_ACTIVE_BG_COLOR NORMAL_BG
+#define DEF_BUTTON_ACTIVE_BG_MONO BLACK
+#define DEF_BUTTON_ACTIVE_FG_COLOR NORMAL_FG
+#define DEF_CHKRAD_ACTIVE_FG_COLOR TEXT_FG
+#define DEF_BUTTON_ACTIVE_FG_MONO WHITE
+#define DEF_BUTTON_BG_COLOR NORMAL_BG
+#define DEF_BUTTON_BG_MONO WHITE
+#define DEF_BUTTON_BITMAP ""
+#define DEF_BUTTON_BORDER_WIDTH "2"
+#define DEF_BUTTON_CURSOR ""
+#define DEF_BUTTON_COMMAND ""
+#define DEF_BUTTON_DEFAULT "disabled"
+#define DEF_BUTTON_DISABLED_FG_COLOR DISABLED
+#define DEF_BUTTON_DISABLED_FG_MONO ""
+#define DEF_BUTTON_FG NORMAL_FG
+#define DEF_CHKRAD_FG TEXT_FG
+#define DEF_BUTTON_FONT CTL_FONT
+#define DEF_BUTTON_HEIGHT "0"
+#define DEF_BUTTON_HIGHLIGHT_BG NORMAL_BG
+#define DEF_BUTTON_HIGHLIGHT HIGHLIGHT
+#define DEF_LABEL_HIGHLIGHT_WIDTH "0"
+#define DEF_BUTTON_HIGHLIGHT_WIDTH "1"
+#define DEF_BUTTON_IMAGE (char *) NULL
+#define DEF_BUTTON_INDICATOR "1"
+#define DEF_BUTTON_JUSTIFY "center"
+#define DEF_BUTTON_OFF_VALUE "0"
+#define DEF_BUTTON_ON_VALUE "1"
+#define DEF_BUTTON_PADX "1"
+#define DEF_LABCHKRAD_PADX "1"
+#define DEF_BUTTON_PADY "1"
+#define DEF_LABCHKRAD_PADY "1"
+#define DEF_BUTTON_RELIEF "raised"
+#define DEF_LABCHKRAD_RELIEF "flat"
+#define DEF_BUTTON_SELECT_COLOR INDICATOR
+#define DEF_BUTTON_SELECT_MONO BLACK
+#define DEF_BUTTON_SELECT_IMAGE (char *) NULL
+#define DEF_BUTTON_STATE "normal"
+#define DEF_LABEL_TAKE_FOCUS "0"
+#define DEF_BUTTON_TAKE_FOCUS (char *) NULL
+#define DEF_BUTTON_TEXT ""
+#define DEF_BUTTON_TEXT_VARIABLE ""
+#define DEF_BUTTON_UNDERLINE "-1"
+#define DEF_BUTTON_VALUE ""
+#define DEF_BUTTON_WIDTH "0"
+#define DEF_BUTTON_WRAP_LENGTH "0"
+#define DEF_RADIOBUTTON_VARIABLE "selectedButton"
+#define DEF_CHECKBUTTON_VARIABLE ""
+
+/*
+ * Defaults for canvases:
+ */
+
+#define DEF_CANVAS_BG_COLOR NORMAL_BG
+#define DEF_CANVAS_BG_MONO WHITE
+#define DEF_CANVAS_BORDER_WIDTH "0"
+#define DEF_CANVAS_CLOSE_ENOUGH "1"
+#define DEF_CANVAS_CONFINE "1"
+#define DEF_CANVAS_CURSOR ""
+#define DEF_CANVAS_HEIGHT "7c"
+#define DEF_CANVAS_HIGHLIGHT_BG NORMAL_BG
+#define DEF_CANVAS_HIGHLIGHT HIGHLIGHT
+#define DEF_CANVAS_HIGHLIGHT_WIDTH "2"
+#define DEF_CANVAS_INSERT_BG NORMAL_FG
+#define DEF_CANVAS_INSERT_BD_COLOR "0"
+#define DEF_CANVAS_INSERT_BD_MONO "0"
+#define DEF_CANVAS_INSERT_OFF_TIME "300"
+#define DEF_CANVAS_INSERT_ON_TIME "600"
+#define DEF_CANVAS_INSERT_WIDTH "2"
+#define DEF_CANVAS_RELIEF "flat"
+#define DEF_CANVAS_SCROLL_REGION ""
+#define DEF_CANVAS_SELECT_COLOR SELECT_BG
+#define DEF_CANVAS_SELECT_MONO BLACK
+#define DEF_CANVAS_SELECT_BD_COLOR "1"
+#define DEF_CANVAS_SELECT_BD_MONO "0"
+#define DEF_CANVAS_SELECT_FG_COLOR SELECT_FG
+#define DEF_CANVAS_SELECT_FG_MONO WHITE
+#define DEF_CANVAS_TAKE_FOCUS (char *) NULL
+#define DEF_CANVAS_WIDTH "10c"
+#define DEF_CANVAS_X_SCROLL_CMD ""
+#define DEF_CANVAS_X_SCROLL_INCREMENT "0"
+#define DEF_CANVAS_Y_SCROLL_CMD ""
+#define DEF_CANVAS_Y_SCROLL_INCREMENT "0"
+
+/*
+ * Defaults for entries:
+ */
+
+#define DEF_ENTRY_BG_COLOR "SystemWindow"
+#define DEF_ENTRY_BG_MONO WHITE
+#define DEF_ENTRY_BORDER_WIDTH "2"
+#define DEF_ENTRY_CURSOR "xterm"
+#define DEF_ENTRY_EXPORT_SELECTION "1"
+#define DEF_ENTRY_FONT CTL_FONT
+#define DEF_ENTRY_FG TEXT_FG
+#define DEF_ENTRY_HIGHLIGHT_BG NORMAL_BG
+#define DEF_ENTRY_HIGHLIGHT HIGHLIGHT
+#define DEF_ENTRY_HIGHLIGHT_WIDTH "0"
+#define DEF_ENTRY_INSERT_BG TEXT_FG
+#define DEF_ENTRY_INSERT_BD_COLOR "0"
+#define DEF_ENTRY_INSERT_BD_MONO "0"
+#define DEF_ENTRY_INSERT_OFF_TIME "300"
+#define DEF_ENTRY_INSERT_ON_TIME "600"
+#define DEF_ENTRY_INSERT_WIDTH "2"
+#define DEF_ENTRY_JUSTIFY "left"
+#define DEF_ENTRY_RELIEF "sunken"
+#define DEF_ENTRY_SCROLL_COMMAND ""
+#define DEF_ENTRY_SELECT_COLOR SELECT_BG
+#define DEF_ENTRY_SELECT_MONO BLACK
+#define DEF_ENTRY_SELECT_BD_COLOR "0"
+#define DEF_ENTRY_SELECT_BD_MONO "0"
+#define DEF_ENTRY_SELECT_FG_COLOR SELECT_FG
+#define DEF_ENTRY_SELECT_FG_MONO WHITE
+#define DEF_ENTRY_SHOW (char *) NULL
+#define DEF_ENTRY_STATE "normal"
+#define DEF_ENTRY_TAKE_FOCUS (char *) NULL
+#define DEF_ENTRY_TEXT_VARIABLE ""
+#define DEF_ENTRY_WIDTH "20"
+
+/*
+ * Defaults for frames:
+ */
+
+#define DEF_FRAME_BG_COLOR NORMAL_BG
+#define DEF_FRAME_BG_MONO WHITE
+#define DEF_FRAME_BORDER_WIDTH "0"
+#define DEF_FRAME_CLASS "Frame"
+#define DEF_FRAME_COLORMAP ""
+#define DEF_FRAME_CONTAINER "0"
+#define DEF_FRAME_CURSOR ""
+#define DEF_FRAME_HEIGHT "0"
+#define DEF_FRAME_HIGHLIGHT_BG NORMAL_BG
+#define DEF_FRAME_HIGHLIGHT HIGHLIGHT
+#define DEF_FRAME_HIGHLIGHT_WIDTH "0"
+#define DEF_FRAME_RELIEF "flat"
+#define DEF_FRAME_TAKE_FOCUS "0"
+#define DEF_FRAME_USE ""
+#define DEF_FRAME_VISUAL ""
+#define DEF_FRAME_WIDTH "0"
+
+/*
+ * Defaults for listboxes:
+ */
+
+#define DEF_LISTBOX_BG_COLOR NORMAL_BG
+#define DEF_LISTBOX_BG_MONO WHITE
+#define DEF_LISTBOX_BORDER_WIDTH "2"
+#define DEF_LISTBOX_CURSOR ""
+#define DEF_LISTBOX_EXPORT_SELECTION "1"
+#define DEF_LISTBOX_FONT CTL_FONT
+#define DEF_LISTBOX_FG NORMAL_FG
+#define DEF_LISTBOX_HEIGHT "10"
+#define DEF_LISTBOX_HIGHLIGHT_BG NORMAL_BG
+#define DEF_LISTBOX_HIGHLIGHT HIGHLIGHT
+#define DEF_LISTBOX_HIGHLIGHT_WIDTH "1"
+#define DEF_LISTBOX_RELIEF "sunken"
+#define DEF_LISTBOX_SCROLL_COMMAND ""
+#define DEF_LISTBOX_SELECT_COLOR SELECT_BG
+#define DEF_LISTBOX_SELECT_MONO BLACK
+#define DEF_LISTBOX_SELECT_BD "1"
+#define DEF_LISTBOX_SELECT_FG_COLOR SELECT_FG
+#define DEF_LISTBOX_SELECT_FG_MONO WHITE
+#define DEF_LISTBOX_SELECT_MODE "browse"
+#define DEF_LISTBOX_SET_GRID "0"
+#define DEF_LISTBOX_TAKE_FOCUS (char *) NULL
+#define DEF_LISTBOX_WIDTH "20"
+
+/*
+ * Defaults for individual entries of menus:
+ */
+
+#define DEF_MENU_ENTRY_ACTIVE_BG (char *) NULL
+#define DEF_MENU_ENTRY_ACTIVE_FG (char *) NULL
+#define DEF_MENU_ENTRY_ACCELERATOR (char *) NULL
+#define DEF_MENU_ENTRY_BG (char *) NULL
+#define DEF_MENU_ENTRY_BITMAP None
+#define DEF_MENU_ENTRY_COLUMN_BREAK "0"
+#define DEF_MENU_ENTRY_COMMAND (char *) NULL
+#define DEF_MENU_ENTRY_FG (char *) NULL
+#define DEF_MENU_ENTRY_FONT (char *) NULL
+#define DEF_MENU_ENTRY_HIDE_MARGIN "0"
+#define DEF_MENU_ENTRY_IMAGE (char *) NULL
+#define DEF_MENU_ENTRY_INDICATOR "1"
+#define DEF_MENU_ENTRY_LABEL (char *) NULL
+#define DEF_MENU_ENTRY_MENU (char *) NULL
+#define DEF_MENU_ENTRY_OFF_VALUE "0"
+#define DEF_MENU_ENTRY_ON_VALUE "1"
+#define DEF_MENU_ENTRY_SELECT_IMAGE (char *) NULL
+#define DEF_MENU_ENTRY_STATE "normal"
+#define DEF_MENU_ENTRY_VALUE (char *) NULL
+#define DEF_MENU_ENTRY_CHECK_VARIABLE (char *) NULL
+#define DEF_MENU_ENTRY_RADIO_VARIABLE "selectedButton"
+#define DEF_MENU_ENTRY_SELECT (char *) NULL
+#define DEF_MENU_ENTRY_UNDERLINE "-1"
+
+/*
+ * Defaults for menus overall:
+ */
+
+#define DEF_MENU_ACTIVE_BG_COLOR SELECT_BG
+#define DEF_MENU_ACTIVE_BG_MONO BLACK
+#define DEF_MENU_ACTIVE_BORDER_WIDTH "0"
+#define DEF_MENU_ACTIVE_FG_COLOR SELECT_FG
+#define DEF_MENU_ACTIVE_FG_MONO WHITE
+#define DEF_MENU_BG_COLOR MENU_BG
+#define DEF_MENU_BG_MONO WHITE
+#define DEF_MENU_BORDER_WIDTH "0"
+#define DEF_MENU_CURSOR "arrow"
+#define DEF_MENU_DISABLED_FG_COLOR DISABLED
+#define DEF_MENU_DISABLED_FG_MONO ""
+#define DEF_MENU_FONT CTL_FONT
+#define DEF_MENU_FG MENU_FG
+#define DEF_MENU_POST_COMMAND ""
+#define DEF_MENU_RELIEF "flat"
+#define DEF_MENU_SELECT_COLOR MENU_FG
+#define DEF_MENU_SELECT_MONO BLACK
+#define DEF_MENU_TAKE_FOCUS "0"
+#define DEF_MENU_TEAROFF "1"
+#define DEF_MENU_TEAROFF_CMD (char *) NULL
+#define DEF_MENU_TITLE ""
+#define DEF_MENU_TYPE "normal"
+
+/*
+ * Defaults for menubuttons:
+ */
+
+#define DEF_MENUBUTTON_ANCHOR "center"
+#define DEF_MENUBUTTON_ACTIVE_BG_COLOR ACTIVE_BG
+#define DEF_MENUBUTTON_ACTIVE_BG_MONO BLACK
+#define DEF_MENUBUTTON_ACTIVE_FG_COLOR NORMAL_FG
+#define DEF_MENUBUTTON_ACTIVE_FG_MONO WHITE
+#define DEF_MENUBUTTON_BG_COLOR NORMAL_BG
+#define DEF_MENUBUTTON_BG_MONO WHITE
+#define DEF_MENUBUTTON_BITMAP ""
+#define DEF_MENUBUTTON_BORDER_WIDTH "2"
+#define DEF_MENUBUTTON_CURSOR ""
+#define DEF_MENUBUTTON_DIRECTION "below"
+#define DEF_MENUBUTTON_DISABLED_FG_COLOR DISABLED
+#define DEF_MENUBUTTON_DISABLED_FG_MONO ""
+#define DEF_MENUBUTTON_FONT CTL_FONT
+#define DEF_MENUBUTTON_FG NORMAL_FG
+#define DEF_MENUBUTTON_HEIGHT "0"
+#define DEF_MENUBUTTON_HIGHLIGHT_BG NORMAL_BG
+#define DEF_MENUBUTTON_HIGHLIGHT HIGHLIGHT
+#define DEF_MENUBUTTON_HIGHLIGHT_WIDTH "0"
+#define DEF_MENUBUTTON_IMAGE (char *) NULL
+#define DEF_MENUBUTTON_INDICATOR "0"
+#define DEF_MENUBUTTON_JUSTIFY "center"
+#define DEF_MENUBUTTON_MENU ""
+#define DEF_MENUBUTTON_PADX "4p"
+#define DEF_MENUBUTTON_PADY "3p"
+#define DEF_MENUBUTTON_RELIEF "flat"
+#define DEF_MENUBUTTON_STATE "normal"
+#define DEF_MENUBUTTON_TAKE_FOCUS "0"
+#define DEF_MENUBUTTON_TEXT ""
+#define DEF_MENUBUTTON_TEXT_VARIABLE ""
+#define DEF_MENUBUTTON_UNDERLINE "-1"
+#define DEF_MENUBUTTON_WIDTH "0"
+#define DEF_MENUBUTTON_WRAP_LENGTH "0"
+
+/*
+ * Defaults for messages:
+ */
+
+#define DEF_MESSAGE_ANCHOR "center"
+#define DEF_MESSAGE_ASPECT "150"
+#define DEF_MESSAGE_BG_COLOR NORMAL_BG
+#define DEF_MESSAGE_BG_MONO WHITE
+#define DEF_MESSAGE_BORDER_WIDTH "2"
+#define DEF_MESSAGE_CURSOR ""
+#define DEF_MESSAGE_FG NORMAL_FG
+#define DEF_MESSAGE_FONT CTL_FONT
+#define DEF_MESSAGE_HIGHLIGHT_BG NORMAL_BG
+#define DEF_MESSAGE_HIGHLIGHT HIGHLIGHT
+#define DEF_MESSAGE_HIGHLIGHT_WIDTH "0"
+#define DEF_MESSAGE_JUSTIFY "left"
+#define DEF_MESSAGE_PADX "-1"
+#define DEF_MESSAGE_PADY "-1"
+#define DEF_MESSAGE_RELIEF "flat"
+#define DEF_MESSAGE_TAKE_FOCUS "0"
+#define DEF_MESSAGE_TEXT ""
+#define DEF_MESSAGE_TEXT_VARIABLE ""
+#define DEF_MESSAGE_WIDTH "0"
+
+/*
+ * Defaults for scales:
+ */
+
+#define DEF_SCALE_ACTIVE_BG_COLOR ACTIVE_BG
+#define DEF_SCALE_ACTIVE_BG_MONO BLACK
+#define DEF_SCALE_BG_COLOR NORMAL_BG
+#define DEF_SCALE_BG_MONO WHITE
+#define DEF_SCALE_BIG_INCREMENT "0"
+#define DEF_SCALE_BORDER_WIDTH "2"
+#define DEF_SCALE_COMMAND ""
+#define DEF_SCALE_CURSOR ""
+#define DEF_SCALE_DIGITS "0"
+#define DEF_SCALE_FONT CTL_FONT
+#define DEF_SCALE_FG_COLOR NORMAL_FG
+#define DEF_SCALE_FG_MONO BLACK
+#define DEF_SCALE_FROM "0"
+#define DEF_SCALE_HIGHLIGHT_BG NORMAL_BG
+#define DEF_SCALE_HIGHLIGHT HIGHLIGHT
+#define DEF_SCALE_HIGHLIGHT_WIDTH "2"
+#define DEF_SCALE_LABEL ""
+#define DEF_SCALE_LENGTH "100"
+#define DEF_SCALE_ORIENT "vertical"
+#define DEF_SCALE_RELIEF "flat"
+#define DEF_SCALE_REPEAT_DELAY "300"
+#define DEF_SCALE_REPEAT_INTERVAL "100"
+#define DEF_SCALE_RESOLUTION "1"
+#define DEF_SCALE_TROUGH_COLOR TROUGH
+#define DEF_SCALE_TROUGH_MONO WHITE
+#define DEF_SCALE_SHOW_VALUE "1"
+#define DEF_SCALE_SLIDER_LENGTH "30"
+#define DEF_SCALE_SLIDER_RELIEF "raised"
+#define DEF_SCALE_STATE "normal"
+#define DEF_SCALE_TAKE_FOCUS (char *) NULL
+#define DEF_SCALE_TICK_INTERVAL "0"
+#define DEF_SCALE_TO "100"
+#define DEF_SCALE_VARIABLE ""
+#define DEF_SCALE_WIDTH "15"
+
+/*
+ * Defaults for scrollbars:
+ */
+
+#define DEF_SCROLLBAR_ACTIVE_BG_COLOR ACTIVE_BG
+#define DEF_SCROLLBAR_ACTIVE_BG_MONO BLACK
+#define DEF_SCROLLBAR_ACTIVE_RELIEF "raised"
+#define DEF_SCROLLBAR_BG_COLOR NORMAL_BG
+#define DEF_SCROLLBAR_BG_MONO WHITE
+#define DEF_SCROLLBAR_BORDER_WIDTH "0"
+#define DEF_SCROLLBAR_COMMAND ""
+#define DEF_SCROLLBAR_CURSOR ""
+#define DEF_SCROLLBAR_EL_BORDER_WIDTH "-1"
+#define DEF_SCROLLBAR_HIGHLIGHT_BG NORMAL_BG
+#define DEF_SCROLLBAR_HIGHLIGHT HIGHLIGHT
+#define DEF_SCROLLBAR_HIGHLIGHT_WIDTH "0"
+#define DEF_SCROLLBAR_JUMP "0"
+#define DEF_SCROLLBAR_ORIENT "vertical"
+#define DEF_SCROLLBAR_RELIEF "sunken"
+#define DEF_SCROLLBAR_REPEAT_DELAY "300"
+#define DEF_SCROLLBAR_REPEAT_INTERVAL "100"
+#define DEF_SCROLLBAR_TAKE_FOCUS (char *) NULL
+#define DEF_SCROLLBAR_TROUGH_COLOR TROUGH
+#define DEF_SCROLLBAR_TROUGH_MONO WHITE
+#define DEF_SCROLLBAR_WIDTH "10"
+
+/*
+ * Defaults for texts:
+ */
+
+#define DEF_TEXT_BG_COLOR "SystemWindow"
+#define DEF_TEXT_BG_MONO WHITE
+#define DEF_TEXT_BORDER_WIDTH "2"
+#define DEF_TEXT_CURSOR "xterm"
+#define DEF_TEXT_FG TEXT_FG
+#define DEF_TEXT_EXPORT_SELECTION "1"
+#define DEF_TEXT_FONT CTL_FONT
+#define DEF_TEXT_HEIGHT "24"
+#define DEF_TEXT_HIGHLIGHT_BG NORMAL_BG
+#define DEF_TEXT_HIGHLIGHT HIGHLIGHT
+#define DEF_TEXT_HIGHLIGHT_WIDTH "0"
+#define DEF_TEXT_INSERT_BG TEXT_FG
+#define DEF_TEXT_INSERT_BD_COLOR "0"
+#define DEF_TEXT_INSERT_BD_MONO "0"
+#define DEF_TEXT_INSERT_OFF_TIME "300"
+#define DEF_TEXT_INSERT_ON_TIME "600"
+#define DEF_TEXT_INSERT_WIDTH "2"
+#define DEF_TEXT_PADX "1"
+#define DEF_TEXT_PADY "1"
+#define DEF_TEXT_RELIEF "sunken"
+#define DEF_TEXT_SELECT_COLOR SELECT_BG
+#define DEF_TEXT_SELECT_MONO BLACK
+#define DEF_TEXT_SELECT_BD_COLOR "0"
+#define DEF_TEXT_SELECT_BD_MONO "0"
+#define DEF_TEXT_SELECT_FG_COLOR SELECT_FG
+#define DEF_TEXT_SELECT_FG_MONO WHITE
+#define DEF_TEXT_SELECT_RELIEF "flat"
+#define DEF_TEXT_SET_GRID "0"
+#define DEF_TEXT_SPACING1 "0"
+#define DEF_TEXT_SPACING2 "0"
+#define DEF_TEXT_SPACING3 "0"
+#define DEF_TEXT_STATE "normal"
+#define DEF_TEXT_TABS ""
+#define DEF_TEXT_TAKE_FOCUS (char *) NULL
+#define DEF_TEXT_WIDTH "80"
+#define DEF_TEXT_WRAP "char"
+#define DEF_TEXT_XSCROLL_COMMAND ""
+#define DEF_TEXT_YSCROLL_COMMAND ""
+
+/*
+ * Defaults for canvas text:
+ */
+
+#define DEF_CANVTEXT_FONT CTL_FONT
+
+/*
+ * Defaults for toplevels (most of the defaults for frames also apply
+ * to toplevels):
+ */
+
+#define DEF_TOPLEVEL_CLASS "Toplevel"
+#define DEF_TOPLEVEL_MENU ""
+#define DEF_TOPLEVEL_SCREEN ""
+
+#endif /* _TKWINDEFAULT */
diff --git a/win/tkWinDialog.c b/win/tkWinDialog.c
new file mode 100644
index 0000000..7d01edb
--- /dev/null
+++ b/win/tkWinDialog.c
@@ -0,0 +1,1050 @@
+/*
+ * tkWinDialog.c --
+ *
+ * Contains the Windows implementation of the common dialog boxes.
+ *
+ * Copyright (c) 1996-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinDialog.c 1.10 97/10/21 11:29:18
+ *
+ */
+
+#include "tkWinInt.h"
+#include "tkFileFilter.h"
+
+#include <commdlg.h> /* includes common dialog functionality */
+#include <dlgs.h> /* includes common dialog template defines */
+#include <cderr.h> /* includes the common dialog error codes */
+
+#if ((TK_MAJOR_VERSION == 4) && (TK_MINOR_VERSION <= 2))
+/*
+ * The following function is implemented on tk4.3 and after only
+ */
+#define Tk_GetHWND TkWinGetHWND
+#endif
+
+#define SAVE_FILE 0
+#define OPEN_FILE 1
+
+/*----------------------------------------------------------------------
+ * MsgTypeInfo --
+ *
+ * This structure stores the type of available message box in an
+ * easy-to-process format. Used by th Tk_MessageBox() function
+ *----------------------------------------------------------------------
+ */
+typedef struct MsgTypeInfo {
+ char * name;
+ int type;
+ int numButtons;
+ char * btnNames[3];
+} MsgTypeInfo;
+
+#define NUM_TYPES 6
+
+static MsgTypeInfo
+msgTypeInfo[NUM_TYPES] = {
+ {"abortretryignore", MB_ABORTRETRYIGNORE, 3, {"abort", "retry", "ignore"}},
+ {"ok", MB_OK, 1, {"ok" }},
+ {"okcancel", MB_OKCANCEL, 2, {"ok", "cancel" }},
+ {"retrycancel", MB_RETRYCANCEL, 2, {"retry", "cancel" }},
+ {"yesno", MB_YESNO, 2, {"yes", "no" }},
+ {"yesnocancel", MB_YESNOCANCEL, 3, {"yes", "no", "cancel"}}
+};
+
+/*
+ * The following structure is used in the GetOpenFileName() and
+ * GetSaveFileName() calls.
+ */
+typedef struct _OpenFileData {
+ Tcl_Interp * interp;
+ TCHAR szFile[MAX_PATH+1];
+} OpenFileData;
+
+/*
+ * The following structure is used in the ChooseColor() call.
+ */
+typedef struct _ChooseColorData {
+ Tcl_Interp * interp;
+ char * title; /* Title of the color dialog */
+} ChooseColorData;
+
+
+static int GetFileName _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int argc, char **argv,
+ int isOpen));
+static UINT CALLBACK ColorDlgHookProc _ANSI_ARGS_((HWND hDlg, UINT uMsg,
+ WPARAM wParam, LPARAM lParam));
+static int MakeFilter _ANSI_ARGS_((Tcl_Interp *interp,
+ OPENFILENAME *ofnPtr, char * string));
+static int ParseFileDlgArgs _ANSI_ARGS_((Tcl_Interp * interp,
+ OPENFILENAME *ofnPtr, int argc, char ** argv,
+ int isOpen));
+static int ProcessCDError _ANSI_ARGS_((Tcl_Interp * interp,
+ DWORD dwErrorCode, HWND hWnd));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EvalArgv --
+ *
+ * Invokes the Tcl procedure with the arguments. argv[0] is set by
+ * the caller of this function. It may be different than cmdName.
+ * The TCL command will see argv[0], not cmdName, as its name if it
+ * invokes [lindex [info level 0] 0]
+ *
+ * Results:
+ * TCL_ERROR if the command does not exist and cannot be autoloaded.
+ * Otherwise, return the result of the evaluation of the command.
+ *
+ * Side effects:
+ * The command may be autoloaded.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+EvalArgv(interp, cmdName, argc, argv)
+ Tcl_Interp *interp; /* Current interpreter. */
+ char * cmdName; /* Name of the TCL command to call */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tcl_CmdInfo cmdInfo;
+
+ if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
+ char * cmdArgv[2];
+
+ /*
+ * This comand is not in the interpreter yet -- looks like we
+ * have to auto-load it
+ */
+ if (!Tcl_GetCommandInfo(interp, "auto_load", &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot execute command \"auto_load\"",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ cmdArgv[0] = "auto_load";
+ cmdArgv[1] = cmdName;
+
+ if ((*cmdInfo.proc)(cmdInfo.clientData, interp, 2, cmdArgv)!= TCL_OK){
+ return TCL_ERROR;
+ }
+
+ if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot auto-load command \"",
+ cmdName, "\"",NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ return (*cmdInfo.proc)(cmdInfo.clientData, interp, argc, argv);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_ChooseColorCmd --
+ *
+ * This procedure implements the color dialog box for the Windows
+ * platform. See the user documentation for details on what it
+ * does.
+ *
+ * Results:
+ * See user documentation.
+ *
+ * Side effects:
+ * A dialog window is created the first time this procedure is called.
+ * This window is not destroyed and will be reused the next time the
+ * application invokes the "tk_chooseColor" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_ChooseColorCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window parent = Tk_MainWindow(interp);
+ ChooseColorData custData;
+ int oldMode;
+ CHOOSECOLOR chooseColor;
+ char * colorStr = NULL;
+ int i;
+ int winCode, tclCode;
+ XColor * colorPtr = NULL;
+ static inited = 0;
+ static long dwCustColors[16];
+ static long oldColor; /* the color selected last time */
+
+ custData.title = NULL;
+
+ if (!inited) {
+ /*
+ * dwCustColors stores the custom color which the user can
+ * modify. We store these colors in a fixed array so that the next
+ * time the color dialog pops up, the same set of custom colors
+ * remain in the dialog.
+ */
+ for (i=0; i<16; i++) {
+ dwCustColors[i] = (RGB(255-i*10, i, i*10)) ;
+ }
+ oldColor = RGB(0xa0,0xa0,0xa0);
+ inited = 1;
+ }
+
+ /*
+ * 1. Parse the arguments
+ */
+
+ chooseColor.lStructSize = sizeof(CHOOSECOLOR) ;
+ chooseColor.hwndOwner = 0; /* filled in below */
+ chooseColor.hInstance = 0;
+ chooseColor.rgbResult = oldColor;
+ chooseColor.lpCustColors = (LPDWORD) dwCustColors ;
+ chooseColor.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ENABLEHOOK;
+ chooseColor.lCustData = (LPARAM)&custData;
+ chooseColor.lpfnHook = ColorDlgHookProc;
+ chooseColor.lpTemplateName = NULL;
+
+ for (i=1; i<argc; i+=2) {
+ int v = i+1;
+ int len = strlen(argv[i]);
+
+ if (strncmp(argv[i], "-initialcolor", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ colorStr = argv[v];
+ }
+ else if (strncmp(argv[i], "-parent", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
+ if (parent == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ else if (strncmp(argv[i], "-title", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ custData.title = argv[v];
+ }
+ else {
+ Tcl_AppendResult(interp, "unknown option \"",
+ argv[i], "\", must be -initialcolor, -parent or -title",
+ NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ if (Tk_WindowId(parent) == None) {
+ Tk_MakeWindowExist(parent);
+ }
+ chooseColor.hwndOwner = Tk_GetHWND(Tk_WindowId(parent));
+
+ if (colorStr != NULL) {
+ colorPtr = Tk_GetColor(interp, Tk_MainWindow(interp), colorStr);
+ if (!colorPtr) {
+ return TCL_ERROR;
+ }
+ chooseColor.rgbResult = RGB((colorPtr->red/0x100),
+ (colorPtr->green/0x100), (colorPtr->blue/0x100));
+ }
+
+ /*
+ * 2. Popup the dialog
+ */
+
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ winCode = ChooseColor(&chooseColor);
+ (void) Tcl_SetServiceMode(oldMode);
+
+ /*
+ * Clear the interp result since anything may have happened during the
+ * modal loop.
+ */
+
+ Tcl_ResetResult(interp);
+
+ /*
+ * 3. Process the result of the dialog
+ */
+ if (winCode) {
+ /*
+ * User has selected a color
+ */
+ char result[100];
+
+ sprintf(result, "#%02x%02x%02x",
+ GetRValue(chooseColor.rgbResult),
+ GetGValue(chooseColor.rgbResult),
+ GetBValue(chooseColor.rgbResult));
+ Tcl_AppendResult(interp, result, NULL);
+ tclCode = TCL_OK;
+
+ oldColor = chooseColor.rgbResult;
+ } else {
+ /*
+ * User probably pressed Cancel, or an error occurred
+ */
+ tclCode = ProcessCDError(interp, CommDlgExtendedError(),
+ chooseColor.hwndOwner);
+ }
+
+ if (colorPtr) {
+ Tk_FreeColor(colorPtr);
+ }
+
+ return tclCode;
+
+ arg_missing:
+ Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
+ NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ColorDlgHookProc --
+ *
+ * Gets called during the execution of the color dialog. It processes
+ * the "interesting" messages that Windows send to the dialog.
+ *
+ * Results:
+ * TRUE if the message has been processed, FALSE otherwise.
+ *
+ * Side effects:
+ * Changes the title of the dialog window when it is popped up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static UINT
+CALLBACK ColorDlgHookProc(hDlg, uMsg, wParam, lParam)
+ HWND hDlg; /* Handle to the color dialog */
+ UINT uMsg; /* Type of message */
+ WPARAM wParam; /* word param, interpretation depends on uMsg*/
+ LPARAM lParam; /* long param, interpretation depends on uMsg*/
+{
+ CHOOSECOLOR * ccPtr;
+ ChooseColorData * pCustData;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ /* Save the pointer to CHOOSECOLOR so that we can use it later */
+ SetWindowLong(hDlg, DWL_USER, lParam);
+
+ /* Set the title string of the dialog */
+ ccPtr = (CHOOSECOLOR*)lParam;
+ pCustData = (ChooseColorData*)(ccPtr->lCustData);
+ if (pCustData->title && *(pCustData->title)) {
+ SetWindowText(hDlg, (LPCSTR)pCustData->title);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetOpenFileCmd --
+ *
+ * This procedure implements the "open file" dialog box for the
+ * Windows platform. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * See user documentation.
+ *
+ * Side effects:
+ * A dialog window is created the first this procedure is called.
+ * This window is not destroyed and will be reused the next time
+ * the application invokes the "tk_getOpenFile" or
+ * "tk_getSaveFile" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_GetOpenFileCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ return GetFileName(clientData, interp, argc, argv, OPEN_FILE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetSaveFileCmd --
+ *
+ * Same as Tk_GetOpenFileCmd but opens a "save file" dialog box
+ * instead
+ *
+ * Results:
+ * Same as Tk_GetOpenFileCmd.
+ *
+ * Side effects:
+ * Same as Tk_GetOpenFileCmd.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_GetSaveFileCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ return GetFileName(clientData, interp, argc, argv, SAVE_FILE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetFileName --
+ *
+ * Calls GetOpenFileName() or GetSaveFileName().
+ *
+ * Results:
+ * See user documentation.
+ *
+ * Side effects:
+ * See user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetFileName(clientData, interp, argc, argv, isOpen)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+ int isOpen; /* true if we should call GetOpenFileName(),
+ * false if we should call GetSaveFileName() */
+{
+ OPENFILENAME openFileName, *ofnPtr;
+ int tclCode, winCode, oldMode;
+ OpenFileData *custData;
+ char buffer[MAX_PATH+1];
+
+ ofnPtr = &openFileName;
+
+ /*
+ * 1. Parse the arguments.
+ */
+ if (ParseFileDlgArgs(interp, ofnPtr, argc, argv, isOpen) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ custData = (OpenFileData*) ofnPtr->lCustData;
+
+ /*
+ * 2. Call the common dialog function.
+ */
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ GetCurrentDirectory(MAX_PATH+1, buffer);
+ if (isOpen) {
+ winCode = GetOpenFileName(ofnPtr);
+ } else {
+ winCode = GetSaveFileName(ofnPtr);
+ }
+ SetCurrentDirectory(buffer);
+ (void) Tcl_SetServiceMode(oldMode);
+
+ /*
+ * Clear the interp result since anything may have happened during the
+ * modal loop.
+ */
+
+ Tcl_ResetResult(interp);
+
+ if (ofnPtr->lpstrInitialDir != NULL) {
+ ckfree((char*) ofnPtr->lpstrInitialDir);
+ }
+
+ /*
+ * 3. Process the results.
+ */
+ if (winCode) {
+ char *p;
+ Tcl_ResetResult(interp);
+
+ for (p = custData->szFile; p && *p; p++) {
+ /*
+ * Change the pathname to the Tcl "normalized" pathname, where
+ * back slashes are used instead of forward slashes
+ */
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+ Tcl_AppendResult(interp, custData->szFile, NULL);
+ tclCode = TCL_OK;
+ } else {
+ tclCode = ProcessCDError(interp, CommDlgExtendedError(),
+ ofnPtr->hwndOwner);
+ }
+
+ if (custData) {
+ ckfree((char*)custData);
+ }
+ if (ofnPtr->lpstrFilter) {
+ ckfree((char*)ofnPtr->lpstrFilter);
+ }
+
+ return tclCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ParseFileDlgArgs --
+ *
+ * Parses the arguments passed to tk_getOpenFile and tk_getSaveFile.
+ *
+ * Results:
+ * A standard TCL return value.
+ *
+ * Side effects:
+ * The OPENFILENAME structure is initialized and modified according
+ * to the arguments.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ParseFileDlgArgs(interp, ofnPtr, argc, argv, isOpen)
+ Tcl_Interp * interp; /* Current interpreter. */
+ OPENFILENAME *ofnPtr; /* Info about the file dialog */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+ int isOpen; /* true if we should call GetOpenFileName(),
+ * false if we should call GetSaveFileName() */
+{
+ OpenFileData * custData;
+ int i;
+ Tk_Window parent = Tk_MainWindow(interp);
+ int doneFilter = 0;
+ int windowsMajorVersion;
+ Tcl_DString buffer;
+
+ custData = (OpenFileData*)ckalloc(sizeof(OpenFileData));
+ custData->interp = interp;
+ strcpy(custData->szFile, "");
+
+ /* Fill in the OPENFILENAME structure to */
+ ofnPtr->lStructSize = sizeof(OPENFILENAME);
+ ofnPtr->hwndOwner = 0; /* filled in below */
+ ofnPtr->lpstrFilter = NULL;
+ ofnPtr->lpstrCustomFilter = NULL;
+ ofnPtr->nMaxCustFilter = 0;
+ ofnPtr->nFilterIndex = 0;
+ ofnPtr->lpstrFile = custData->szFile;
+ ofnPtr->nMaxFile = sizeof(custData->szFile);
+ ofnPtr->lpstrFileTitle = NULL;
+ ofnPtr->nMaxFileTitle = 0;
+ ofnPtr->lpstrInitialDir = NULL;
+ ofnPtr->lpstrTitle = NULL;
+ ofnPtr->nFileOffset = 0;
+ ofnPtr->nFileExtension = 0;
+ ofnPtr->lpstrDefExt = NULL;
+ ofnPtr->lpfnHook = NULL;
+ ofnPtr->lCustData = (DWORD)custData;
+ ofnPtr->lpTemplateName = NULL;
+ ofnPtr->Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
+
+ windowsMajorVersion = LOBYTE(LOWORD(GetVersion()));
+ if (windowsMajorVersion >= 4) {
+ /*
+ * Use the "explorer" style file selection box on platforms that
+ * support it (Win95 and NT4.0, both have a major version number
+ * of 4)
+ */
+ ofnPtr->Flags |= OFN_EXPLORER;
+ }
+
+
+ if (isOpen) {
+ ofnPtr->Flags |= OFN_FILEMUSTEXIST;
+ } else {
+ ofnPtr->Flags |= OFN_OVERWRITEPROMPT;
+ }
+
+ for (i=1; i<argc; i+=2) {
+ int v = i+1;
+ int len = strlen(argv[i]);
+
+ if (strncmp(argv[i], "-defaultextension", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ ofnPtr->lpstrDefExt = argv[v];
+ if (ofnPtr->lpstrDefExt[0] == '.') {
+ /* Windows will insert the dot for us */
+ ofnPtr->lpstrDefExt ++;
+ }
+ }
+ else if (strncmp(argv[i], "-filetypes", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ if (MakeFilter(interp, ofnPtr, argv[v]) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ doneFilter = 1;
+ }
+ else if (strncmp(argv[i], "-initialdir", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ if (Tcl_TranslateFileName(interp, argv[v], &buffer) == NULL) {
+ return TCL_ERROR;
+ }
+ ofnPtr->lpstrInitialDir = ckalloc(Tcl_DStringLength(&buffer)+1);
+ strcpy((char*)ofnPtr->lpstrInitialDir, Tcl_DStringValue(&buffer));
+ Tcl_DStringFree(&buffer);
+ }
+ else if (strncmp(argv[i], "-initialfile", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ if (Tcl_TranslateFileName(interp, argv[v], &buffer) == NULL) {
+ return TCL_ERROR;
+ }
+ strcpy(ofnPtr->lpstrFile, Tcl_DStringValue(&buffer));
+ Tcl_DStringFree(&buffer);
+ }
+ else if (strncmp(argv[i], "-parent", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
+ if (parent == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ else if (strncmp(argv[i], "-title", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ ofnPtr->lpstrTitle = argv[v];
+ }
+ else {
+ Tcl_AppendResult(interp, "unknown option \"",
+ argv[i], "\", must be -defaultextension, ",
+ "-filetypes, -initialdir, -initialfile, -parent or -title",
+ NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ if (!doneFilter) {
+ if (MakeFilter(interp, ofnPtr, "") != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ if (Tk_WindowId(parent) == None) {
+ Tk_MakeWindowExist(parent);
+ }
+ ofnPtr->hwndOwner = Tk_GetHWND(Tk_WindowId(parent));
+
+ return TCL_OK;
+
+ arg_missing:
+ Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
+ NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeFilter --
+ *
+ * Allocate a buffer to store the filters in a format understood by
+ * Windows
+ *
+ * Results:
+ * A standard TCL return value.
+ *
+ * Side effects:
+ * ofnPtr->lpstrFilter is modified.
+ *
+ *----------------------------------------------------------------------
+ */
+static int MakeFilter(interp, ofnPtr, string)
+ Tcl_Interp *interp; /* Current interpreter. */
+ OPENFILENAME *ofnPtr; /* Info about the file dialog */
+ char *string; /* String value of the -filetypes option */
+{
+ char *filterStr;
+ char *p;
+ int pass;
+ FileFilterList flist;
+ FileFilter *filterPtr;
+
+ TkInitFileFilters(&flist);
+ if (TkGetFileFilters(interp, &flist, string, 1) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (flist.filters == NULL) {
+ /*
+ * Use "All Files (*.*) as the default filter is none is specified
+ */
+ char *defaultFilter = "All Files (*.*)";
+
+ p = filterStr = (char*)ckalloc(30 * sizeof(char));
+
+ strcpy(p, defaultFilter);
+ p+= strlen(defaultFilter);
+
+ *p++ = '\0';
+ *p++ = '*';
+ *p++ = '.';
+ *p++ = '*';
+ *p++ = '\0';
+ *p++ = '\0';
+ *p = '\0';
+
+ } else {
+ /* We format the filetype into a string understood by Windows:
+ * {"Text Documents" {.doc .txt} {TEXT}} becomes
+ * "Text Documents (*.doc,*.txt)\0*.doc;*.txt\0"
+ *
+ * See the Windows OPENFILENAME manual page for details on the filter
+ * string format.
+ */
+
+ /*
+ * Since we may only add asterisks (*) to the filter, we need at most
+ * twice the size of the string to format the filter
+ */
+ filterStr = ckalloc(strlen(string) * 3);
+
+ for (filterPtr = flist.filters, p = filterStr; filterPtr;
+ filterPtr = filterPtr->next) {
+ char *sep;
+ FileFilterClause *clausePtr;
+
+ /*
+ * First, put in the name of the file type
+ */
+ strcpy(p, filterPtr->name);
+ p+= strlen(filterPtr->name);
+ *p++ = ' ';
+ *p++ = '(';
+
+ for (pass = 1; pass <= 2; pass++) {
+ /*
+ * In the first pass, we format the extensions in the
+ * name field. In the second pass, we format the extensions in
+ * the filter pattern field
+ */
+ sep = "";
+ for (clausePtr=filterPtr->clauses;clausePtr;
+ clausePtr=clausePtr->next) {
+ GlobPattern *globPtr;
+
+
+ for (globPtr=clausePtr->patterns; globPtr;
+ globPtr=globPtr->next) {
+ strcpy(p, sep);
+ p+= strlen(sep);
+ strcpy(p, globPtr->pattern);
+ p+= strlen(globPtr->pattern);
+
+ if (pass==1) {
+ sep = ",";
+ } else {
+ sep = ";";
+ }
+ }
+ }
+ if (pass == 1) {
+ if (pass == 1) {
+ *p ++ = ')';
+ }
+ }
+ *p ++ = '\0';
+ }
+ }
+
+ /*
+ * Windows requires the filter string to be ended by two NULL
+ * characters.
+ */
+ *p++ = '\0';
+ *p = '\0';
+ }
+
+ if (ofnPtr->lpstrFilter != NULL) {
+ ckfree((char*)ofnPtr->lpstrFilter);
+ }
+ ofnPtr->lpstrFilter = filterStr;
+
+ TkFreeFileFilters(&flist);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MessageBoxCmd --
+ *
+ * This procedure implements the MessageBox window for the
+ * Windows platform. See the user documentation for details on what
+ * it does.
+ *
+ * Results:
+ * See user documentation.
+ *
+ * Side effects:
+ * None. The MessageBox window will be destroy before this procedure
+ * returns.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_MessageBoxCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int flags;
+ Tk_Window parent = Tk_MainWindow(interp);
+ HWND hWnd;
+ char *message = "";
+ char *title = "";
+ int icon = MB_ICONINFORMATION;
+ int type = MB_OK;
+ int i, j;
+ char *result;
+ int code, oldMode;
+ char *defaultBtn = NULL;
+ int defaultBtnIdx = -1;
+
+ for (i=1; i<argc; i+=2) {
+ int v = i+1;
+ int len = strlen(argv[i]);
+
+ if (strncmp(argv[i], "-default", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ defaultBtn = argv[v];
+ }
+ else if (strncmp(argv[i], "-icon", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ if (strcmp(argv[v], "error") == 0) {
+ icon = MB_ICONERROR;
+ }
+ else if (strcmp(argv[v], "info") == 0) {
+ icon = MB_ICONINFORMATION;
+ }
+ else if (strcmp(argv[v], "question") == 0) {
+ icon = MB_ICONQUESTION;
+ }
+ else if (strcmp(argv[v], "warning") == 0) {
+ icon = MB_ICONWARNING;
+ }
+ else {
+ Tcl_AppendResult(interp, "invalid icon \"", argv[v],
+ "\", must be error, info, question or warning", NULL);
+ return TCL_ERROR;
+ }
+ }
+ else if (strncmp(argv[i], "-message", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ message = argv[v];
+ }
+ else if (strncmp(argv[i], "-parent", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ parent=Tk_NameToWindow(interp, argv[v], Tk_MainWindow(interp));
+ if (parent == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ else if (strncmp(argv[i], "-title", len)==0) {
+ if (v==argc) {goto arg_missing;}
+
+ title = argv[v];
+ }
+ else if (strncmp(argv[i], "-type", len)==0) {
+ int found = 0;
+
+ if (v==argc) {goto arg_missing;}
+
+ for (j=0; j<NUM_TYPES; j++) {
+ if (strcmp(argv[v], msgTypeInfo[j].name) == 0) {
+ type = msgTypeInfo[j].type;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ Tcl_AppendResult(interp, "invalid message box type \"",
+ argv[v], "\", must be abortretryignore, ok, ",
+ "okcancel, retrycancel, yesno or yesnocancel", NULL);
+ return TCL_ERROR;
+ }
+ }
+ else {
+ Tcl_AppendResult(interp, "unknown option \"",
+ argv[i], "\", must be -default, -icon, ",
+ "-message, -parent, -title or -type", NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ /* Make sure we have a valid hWnd to act as the parent of this message box
+ */
+ if (Tk_WindowId(parent) == None) {
+ Tk_MakeWindowExist(parent);
+ }
+ hWnd = Tk_GetHWND(Tk_WindowId(parent));
+
+ if (defaultBtn != NULL) {
+ for (i=0; i<NUM_TYPES; i++) {
+ if (type == msgTypeInfo[i].type) {
+ for (j=0; j<msgTypeInfo[i].numButtons; j++) {
+ if (strcmp(defaultBtn, msgTypeInfo[i].btnNames[j])==0) {
+ defaultBtnIdx = j;
+ break;
+ }
+ }
+ if (defaultBtnIdx < 0) {
+ Tcl_AppendResult(interp, "invalid default button \"",
+ defaultBtn, "\"", NULL);
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+
+ switch (defaultBtnIdx) {
+ case 0: flags = MB_DEFBUTTON1; break;
+ case 1: flags = MB_DEFBUTTON2; break;
+ case 2: flags = MB_DEFBUTTON3; break;
+ case 3: flags = MB_DEFBUTTON4; break;
+ }
+ } else {
+ flags = 0;
+ }
+
+ flags |= icon | type;
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ code = MessageBox(hWnd, message, title, flags|MB_SYSTEMMODAL);
+ (void) Tcl_SetServiceMode(oldMode);
+
+ switch (code) {
+ case IDABORT: result = "abort"; break;
+ case IDCANCEL: result = "cancel"; break;
+ case IDIGNORE: result = "ignore"; break;
+ case IDNO: result = "no"; break;
+ case IDOK: result = "ok"; break;
+ case IDRETRY: result = "retry"; break;
+ case IDYES: result = "yes"; break;
+ default: result = "";
+ }
+
+ /*
+ * When we come to here interp->result may have been changed by some
+ * background scripts. Call Tcl_SetResult() to make sure that any stuff
+ * lingering in interp->result will not appear in the result of
+ * this command.
+ */
+
+ Tcl_SetResult(interp, result, TCL_STATIC);
+ return TCL_OK;
+
+ arg_missing:
+ Tcl_AppendResult(interp, "value for \"", argv[argc-1], "\" missing",
+ NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ProcessCDError --
+ *
+ * This procedure gets called if a Windows-specific error message
+ * has occurred during the execution of a common dialog or the
+ * user has pressed the CANCEL button.
+ *
+ * Results:
+ * If an error has indeed happened, returns a standard TCL result
+ * that reports the error code in string format. If the user has
+ * pressed the CANCEL button (dwErrorCode == 0), resets
+ * interp->result to the empty string.
+ *
+ * Side effects:
+ * interp->result is changed.
+ *
+ *----------------------------------------------------------------------
+ */
+static int ProcessCDError(interp, dwErrorCode, hWnd)
+ Tcl_Interp * interp; /* Current interpreter. */
+ DWORD dwErrorCode; /* The Windows-specific error code */
+ HWND hWnd; /* window in which the error happened*/
+{
+ char *string;
+
+ Tcl_ResetResult(interp);
+
+ switch(dwErrorCode) {
+ case 0: /* User has hit CANCEL */
+ return TCL_OK;
+
+ case CDERR_DIALOGFAILURE: string="CDERR_DIALOGFAILURE"; break;
+ case CDERR_STRUCTSIZE: string="CDERR_STRUCTSIZE"; break;
+ case CDERR_INITIALIZATION: string="CDERR_INITIALIZATION"; break;
+ case CDERR_NOTEMPLATE: string="CDERR_NOTEMPLATE"; break;
+ case CDERR_NOHINSTANCE: string="CDERR_NOHINSTANCE"; break;
+ case CDERR_LOADSTRFAILURE: string="CDERR_LOADSTRFAILURE"; break;
+ case CDERR_FINDRESFAILURE: string="CDERR_FINDRESFAILURE"; break;
+ case CDERR_LOADRESFAILURE: string="CDERR_LOADRESFAILURE"; break;
+ case CDERR_LOCKRESFAILURE: string="CDERR_LOCKRESFAILURE"; break;
+ case CDERR_MEMALLOCFAILURE: string="CDERR_MEMALLOCFAILURE"; break;
+ case CDERR_MEMLOCKFAILURE: string="CDERR_MEMLOCKFAILURE"; break;
+ case CDERR_NOHOOK: string="CDERR_NOHOOK"; break;
+ case PDERR_SETUPFAILURE: string="PDERR_SETUPFAILURE"; break;
+ case PDERR_PARSEFAILURE: string="PDERR_PARSEFAILURE"; break;
+ case PDERR_RETDEFFAILURE: string="PDERR_RETDEFFAILURE"; break;
+ case PDERR_LOADDRVFAILURE: string="PDERR_LOADDRVFAILURE"; break;
+ case PDERR_GETDEVMODEFAIL: string="PDERR_GETDEVMODEFAIL"; break;
+ case PDERR_INITFAILURE: string="PDERR_INITFAILURE"; break;
+ case PDERR_NODEVICES: string="PDERR_NODEVICES"; break;
+ case PDERR_NODEFAULTPRN: string="PDERR_NODEFAULTPRN"; break;
+ case PDERR_DNDMMISMATCH: string="PDERR_DNDMMISMATCH"; break;
+ case PDERR_CREATEICFAILURE: string="PDERR_CREATEICFAILURE"; break;
+ case PDERR_PRINTERNOTFOUND: string="PDERR_PRINTERNOTFOUND"; break;
+ case CFERR_NOFONTS: string="CFERR_NOFONTS"; break;
+ case FNERR_SUBCLASSFAILURE: string="FNERR_SUBCLASSFAILURE"; break;
+ case FNERR_INVALIDFILENAME: string="FNERR_INVALIDFILENAME"; break;
+ case FNERR_BUFFERTOOSMALL: string="FNERR_BUFFERTOOSMALL"; break;
+
+ default:
+ string="unknown error";
+ }
+
+ Tcl_AppendResult(interp, "Win32 internal error: ", string, NULL);
+ return TCL_ERROR;
+}
diff --git a/win/tkWinDraw.c b/win/tkWinDraw.c
new file mode 100644
index 0000000..e972365
--- /dev/null
+++ b/win/tkWinDraw.c
@@ -0,0 +1,1264 @@
+/*
+ * tkWinDraw.c --
+ *
+ * This file contains the Xlib emulation functions pertaining to
+ * actually drawing objects on a window.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinDraw.c 1.30 97/03/21 11:20:05
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * These macros convert between X's bizarre angle units to radians.
+ */
+
+#define PI 3.14159265358979
+#define XAngleToRadians(a) ((double)(a) / 64 * PI / 180);
+
+/*
+ * Translation table between X gc functions and Win32 raster op modes.
+ */
+
+int tkpWinRopModes[] = {
+ R2_BLACK, /* GXclear */
+ R2_MASKPEN, /* GXand */
+ R2_MASKPENNOT, /* GXandReverse */
+ R2_COPYPEN, /* GXcopy */
+ R2_MASKNOTPEN, /* GXandInverted */
+ R2_NOT, /* GXnoop */
+ R2_XORPEN, /* GXxor */
+ R2_MERGEPEN, /* GXor */
+ R2_NOTMERGEPEN, /* GXnor */
+ R2_NOTXORPEN, /* GXequiv */
+ R2_NOT, /* GXinvert */
+ R2_MERGEPENNOT, /* GXorReverse */
+ R2_NOTCOPYPEN, /* GXcopyInverted */
+ R2_MERGENOTPEN, /* GXorInverted */
+ R2_NOTMASKPEN, /* GXnand */
+ R2_WHITE /* GXset */
+};
+
+/*
+ * Translation table between X gc functions and Win32 BitBlt op modes. Some
+ * of the operations defined in X don't have names, so we have to construct
+ * new opcodes for those functions. This is arcane and probably not all that
+ * useful, but at least it's accurate.
+ */
+
+#define NOTSRCAND (DWORD)0x00220326 /* dest = (NOT source) AND dest */
+#define NOTSRCINVERT (DWORD)0x00990066 /* dest = (NOT source) XOR dest */
+#define SRCORREVERSE (DWORD)0x00DD0228 /* dest = source OR (NOT dest) */
+#define SRCNAND (DWORD)0x007700E6 /* dest = NOT (source AND dest) */
+
+static int bltModes[] = {
+ BLACKNESS, /* GXclear */
+ SRCAND, /* GXand */
+ SRCERASE, /* GXandReverse */
+ SRCCOPY, /* GXcopy */
+ NOTSRCAND, /* GXandInverted */
+ PATCOPY, /* GXnoop */
+ SRCINVERT, /* GXxor */
+ SRCPAINT, /* GXor */
+ NOTSRCERASE, /* GXnor */
+ NOTSRCINVERT, /* GXequiv */
+ DSTINVERT, /* GXinvert */
+ SRCORREVERSE, /* GXorReverse */
+ NOTSRCCOPY, /* GXcopyInverted */
+ MERGEPAINT, /* GXorInverted */
+ SRCNAND, /* GXnand */
+ WHITENESS /* GXset */
+};
+
+/*
+ * The following raster op uses the source bitmap as a mask for the
+ * pattern. This is used to draw in a foreground color but leave the
+ * background color transparent.
+ */
+
+#define MASKPAT 0x00E20746 /* dest = (src & pat) | (!src & dst) */
+
+/*
+ * The following two raster ops are used to copy the foreground and background
+ * bits of a source pattern as defined by a stipple used as the pattern.
+ */
+
+#define COPYFG 0x00CA0749 /* dest = (pat & src) | (!pat & dst) */
+#define COPYBG 0x00AC0744 /* dest = (!pat & src) | (pat & dst) */
+
+/*
+ * Macros used later in the file.
+ */
+
+#define MIN(a,b) ((a>b) ? b : a)
+#define MAX(a,b) ((a<b) ? b : a)
+
+/*
+ * The followng typedef is used to pass Windows GDI drawing functions.
+ */
+
+typedef BOOL (CALLBACK *WinDrawFunc) _ANSI_ARGS_((HDC dc,
+ CONST POINT* points, int npoints));
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static POINT * ConvertPoints _ANSI_ARGS_((XPoint *points, int npoints,
+ int mode, RECT *bbox));
+static void DrawOrFillArc _ANSI_ARGS_((Display *display,
+ Drawable d, GC gc, int x, int y,
+ unsigned int width, unsigned int height,
+ int start, int extent, int fill));
+static void RenderObject _ANSI_ARGS_((HDC dc, GC gc,
+ XPoint* points, int npoints, int mode, HPEN pen,
+ WinDrawFunc func));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetDrawableDC --
+ *
+ * Retrieve the DC from a drawable.
+ *
+ * Results:
+ * Returns the window DC for windows. Returns a new memory DC
+ * for pixmaps.
+ *
+ * Side effects:
+ * Sets up the palette for the device context, and saves the old
+ * device context state in the passed in TkWinDCState structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HDC
+TkWinGetDrawableDC(display, d, state)
+ Display *display;
+ Drawable d;
+ TkWinDCState* state;
+{
+ HDC dc;
+ TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+ Colormap cmap;
+
+ if (twdPtr->type == TWD_WINDOW) {
+ TkWindow *winPtr = twdPtr->window.winPtr;
+
+ dc = GetDC(twdPtr->window.handle);
+ if (winPtr == NULL) {
+ cmap = DefaultColormap(display, DefaultScreen(display));
+ } else {
+ cmap = winPtr->atts.colormap;
+ }
+ } else if (twdPtr->type == TWD_WINDC) {
+ dc = twdPtr->winDC.hdc;
+ cmap = DefaultColormap(display, DefaultScreen(display));
+ } else {
+ dc = CreateCompatibleDC(NULL);
+ SelectObject(dc, twdPtr->bitmap.handle);
+ cmap = twdPtr->bitmap.colormap;
+ }
+ state->palette = TkWinSelectPalette(dc, cmap);
+ return dc;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinReleaseDrawableDC --
+ *
+ * Frees the resources associated with a drawable's DC.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Restores the old bitmap handle to the memory DC for pixmaps.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinReleaseDrawableDC(d, dc, state)
+ Drawable d;
+ HDC dc;
+ TkWinDCState *state;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+ SelectPalette(dc, state->palette, TRUE);
+ RealizePalette(dc);
+ if (twdPtr->type == TWD_WINDOW) {
+ ReleaseDC(TkWinGetHWND(d), dc);
+ } else if (twdPtr->type == TWD_BITMAP) {
+ DeleteDC(dc);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConvertPoints --
+ *
+ * Convert an array of X points to an array of Win32 points.
+ *
+ * Results:
+ * Returns the converted array of POINTs.
+ *
+ * Side effects:
+ * Allocates a block of memory that should not be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static POINT *
+ConvertPoints(points, npoints, mode, bbox)
+ XPoint *points;
+ int npoints;
+ int mode; /* CoordModeOrigin or CoordModePrevious. */
+ RECT *bbox; /* Bounding box of points. */
+{
+ static POINT *winPoints = NULL; /* Array of points that is reused. */
+ static int nWinPoints = -1; /* Current size of point array. */
+ int i;
+
+ /*
+ * To avoid paying the cost of a malloc on every drawing routine,
+ * we reuse the last array if it is large enough.
+ */
+
+ if (npoints > nWinPoints) {
+ if (winPoints != NULL) {
+ ckfree((char *) winPoints);
+ }
+ winPoints = (POINT *) ckalloc(sizeof(POINT) * npoints);
+ if (winPoints == NULL) {
+ nWinPoints = -1;
+ return NULL;
+ }
+ nWinPoints = npoints;
+ }
+
+ bbox->left = bbox->right = points[0].x;
+ bbox->top = bbox->bottom = points[0].y;
+
+ if (mode == CoordModeOrigin) {
+ for (i = 0; i < npoints; i++) {
+ winPoints[i].x = points[i].x;
+ winPoints[i].y = points[i].y;
+ bbox->left = MIN(bbox->left, winPoints[i].x);
+ bbox->right = MAX(bbox->right, winPoints[i].x);
+ bbox->top = MIN(bbox->top, winPoints[i].y);
+ bbox->bottom = MAX(bbox->bottom, winPoints[i].y);
+ }
+ } else {
+ winPoints[0].x = points[0].x;
+ winPoints[0].y = points[0].y;
+ for (i = 1; i < npoints; i++) {
+ winPoints[i].x = winPoints[i-1].x + points[i].x;
+ winPoints[i].y = winPoints[i-1].y + points[i].y;
+ bbox->left = MIN(bbox->left, winPoints[i].x);
+ bbox->right = MAX(bbox->right, winPoints[i].x);
+ bbox->top = MIN(bbox->top, winPoints[i].y);
+ bbox->bottom = MAX(bbox->bottom, winPoints[i].y);
+ }
+ }
+ return winPoints;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XCopyArea --
+ *
+ * Copies data from one drawable to another using block transfer
+ * routines.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Data is moved from a window or bitmap to a second window or
+ * bitmap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y)
+ Display* display;
+ Drawable src;
+ Drawable dest;
+ GC gc;
+ int src_x, src_y;
+ unsigned int width, height;
+ int dest_x, dest_y;
+{
+ HDC srcDC, destDC;
+ TkWinDCState srcState, destState;
+ TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
+
+ srcDC = TkWinGetDrawableDC(display, src, &srcState);
+
+ if (src != dest) {
+ destDC = TkWinGetDrawableDC(display, dest, &destState);
+ } else {
+ destDC = srcDC;
+ }
+
+ if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
+ SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+ OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+ }
+
+ BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
+ bltModes[gc->function]);
+
+ SelectClipRgn(destDC, NULL);
+
+ if (src != dest) {
+ TkWinReleaseDrawableDC(dest, destDC, &destState);
+ }
+ TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XCopyPlane --
+ *
+ * Copies a bitmap from a source drawable to a destination
+ * drawable. The plane argument specifies which bit plane of
+ * the source contains the bitmap. Note that this implementation
+ * ignores the gc->function.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the destination drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XCopyPlane(display, src, dest, gc, src_x, src_y, width, height, dest_x,
+ dest_y, plane)
+ Display* display;
+ Drawable src;
+ Drawable dest;
+ GC gc;
+ int src_x, src_y;
+ unsigned int width, height;
+ int dest_x, dest_y;
+ unsigned long plane;
+{
+ HDC srcDC, destDC;
+ TkWinDCState srcState, destState;
+ HBRUSH bgBrush, fgBrush, oldBrush;
+ TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask;
+
+ display->request++;
+
+ if (plane != 1) {
+ panic("Unexpected plane specified for XCopyPlane");
+ }
+
+ srcDC = TkWinGetDrawableDC(display, src, &srcState);
+
+ if (src != dest) {
+ destDC = TkWinGetDrawableDC(display, dest, &destState);
+ } else {
+ destDC = srcDC;
+ }
+
+ if (clipPtr == NULL || clipPtr->type == TKP_CLIP_REGION) {
+
+ /*
+ * Case 1: opaque bitmaps. Windows handles the conversion
+ * from one bit to multiple bits by setting 0 to the
+ * foreground color, and 1 to the background color (seems
+ * backwards, but there you are).
+ */
+
+ if (clipPtr && clipPtr->type == TKP_CLIP_REGION) {
+ SelectClipRgn(destDC, (HRGN) clipPtr->value.region);
+ OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin);
+ }
+
+ SetBkMode(destDC, OPAQUE);
+ SetBkColor(destDC, gc->foreground);
+ SetTextColor(destDC, gc->background);
+ BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
+ SRCCOPY);
+
+ SelectClipRgn(destDC, NULL);
+ } else if (clipPtr->type == TKP_CLIP_PIXMAP) {
+ if (clipPtr->value.pixmap == src) {
+
+ /*
+ * Case 2: transparent bitmaps are handled by setting the
+ * destination to the foreground color whenever the source
+ * pixel is set.
+ */
+
+ fgBrush = CreateSolidBrush(gc->foreground);
+ oldBrush = SelectObject(destDC, fgBrush);
+ BitBlt(destDC, dest_x, dest_y, width, height, srcDC, src_x, src_y,
+ MASKPAT);
+ SelectObject(destDC, oldBrush);
+ DeleteObject(fgBrush);
+ } else {
+
+ /*
+ * Case 3: two arbitrary bitmaps. Copy the source rectangle
+ * into a color pixmap. Use the result as a brush when
+ * copying the clip mask into the destination.
+ */
+
+ HDC memDC, maskDC;
+ HBITMAP bitmap;
+ TkWinDCState maskState;
+
+ fgBrush = CreateSolidBrush(gc->foreground);
+ bgBrush = CreateSolidBrush(gc->background);
+ maskDC = TkWinGetDrawableDC(display, clipPtr->value.pixmap,
+ &maskState);
+ memDC = CreateCompatibleDC(destDC);
+ bitmap = CreateBitmap(width, height, 1, 1, NULL);
+ SelectObject(memDC, bitmap);
+
+ /*
+ * Set foreground bits. We create a new bitmap containing
+ * (source AND mask), then use it to set the foreground color
+ * into the destination.
+ */
+
+ BitBlt(memDC, 0, 0, width, height, srcDC, src_x, src_y, SRCCOPY);
+ BitBlt(memDC, 0, 0, width, height, maskDC,
+ dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
+ SRCAND);
+ oldBrush = SelectObject(destDC, fgBrush);
+ BitBlt(destDC, dest_x, dest_y, width, height, memDC, 0, 0,
+ MASKPAT);
+
+ /*
+ * Set background bits. Same as foreground, except we use
+ * ((NOT source) AND mask) and the background brush.
+ */
+
+ BitBlt(memDC, 0, 0, width, height, srcDC, src_x, src_y,
+ NOTSRCCOPY);
+ BitBlt(memDC, 0, 0, width, height, maskDC,
+ dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin,
+ SRCAND);
+ SelectObject(destDC, bgBrush);
+ BitBlt(destDC, dest_x, dest_y, width, height, memDC, 0, 0,
+ MASKPAT);
+
+ TkWinReleaseDrawableDC(clipPtr->value.pixmap, maskDC, &maskState);
+ SelectObject(destDC, oldBrush);
+ DeleteDC(memDC);
+ DeleteObject(bitmap);
+ DeleteObject(fgBrush);
+ DeleteObject(bgBrush);
+ }
+ }
+ if (src != dest) {
+ TkWinReleaseDrawableDC(dest, destDC, &destState);
+ }
+ TkWinReleaseDrawableDC(src, srcDC, &srcState);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkPutImage --
+ *
+ * Copies a subimage from an in-memory image to a rectangle of
+ * of the specified drawable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws the image on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkPutImage(colors, ncolors, display, d, gc, image, src_x, src_y, dest_x,
+ dest_y, width, height)
+ unsigned long *colors; /* Array of pixel values used by this
+ * image. May be NULL. */
+ int ncolors; /* Number of colors used, or 0. */
+ Display* display;
+ Drawable d; /* Destination drawable. */
+ GC gc;
+ XImage* image; /* Source image. */
+ int src_x, src_y; /* Offset of subimage. */
+ int dest_x, dest_y; /* Position of subimage origin in
+ * drawable. */
+ unsigned int width, height; /* Dimensions of subimage. */
+{
+ HDC dc, dcMem;
+ TkWinDCState state;
+ BITMAPINFO *infoPtr;
+ HBITMAP bitmap;
+ char *data;
+
+ display->request++;
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ dcMem = CreateCompatibleDC(dc);
+
+ if (image->bits_per_pixel == 1) {
+ /*
+ * If the image isn't in the right format, we have to copy
+ * it into a new buffer in MSBFirst and word-aligned format.
+ */
+
+ if ((image->bitmap_bit_order != MSBFirst)
+ || (image->bitmap_pad != sizeof(WORD))) {
+ data = TkAlignImageData(image, sizeof(WORD), MSBFirst);
+ bitmap = CreateBitmap(image->width, image->height, 1, 1, data);
+ ckfree(data);
+ } else {
+ bitmap = CreateBitmap(image->width, image->height, 1, 1,
+ image->data);
+ }
+ SetTextColor(dc, gc->foreground);
+ SetBkColor(dc, gc->background);
+ } else {
+ int i, usePalette;
+
+ /*
+ * Do not use a palette for TrueColor images.
+ */
+
+ usePalette = (image->bits_per_pixel < 16);
+
+ if (usePalette) {
+ infoPtr = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER)
+ + sizeof(RGBQUAD)*ncolors);
+ } else {
+ infoPtr = (BITMAPINFO*) ckalloc(sizeof(BITMAPINFOHEADER));
+ }
+
+ infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ infoPtr->bmiHeader.biWidth = image->width;
+
+ /*
+ * The following code works around a bug in Win32s. CreateDIBitmap
+ * fails under Win32s for top-down images. So we have to reverse the
+ * order of the scanlines. If we are not running under Win32s, we can
+ * just declare the image to be top-down.
+ */
+
+ if (tkpIsWin32s) {
+ int y;
+ char *srcPtr, *dstPtr, *temp;
+
+ temp = ckalloc((unsigned) image->bytes_per_line);
+ srcPtr = image->data;
+ dstPtr = image->data+(image->bytes_per_line * (image->height - 1));
+ for (y = 0; y < (image->height/2); y++) {
+ memcpy(temp, srcPtr, image->bytes_per_line);
+ memcpy(srcPtr, dstPtr, image->bytes_per_line);
+ memcpy(dstPtr, temp, image->bytes_per_line);
+ srcPtr += image->bytes_per_line;
+ dstPtr -= image->bytes_per_line;
+ }
+ ckfree(temp);
+ infoPtr->bmiHeader.biHeight = image->height; /* Bottom-up order */
+ } else {
+ infoPtr->bmiHeader.biHeight = -image->height; /* Top-down order */
+ }
+ infoPtr->bmiHeader.biPlanes = 1;
+ infoPtr->bmiHeader.biBitCount = image->bits_per_pixel;
+ infoPtr->bmiHeader.biCompression = BI_RGB;
+ infoPtr->bmiHeader.biSizeImage = 0;
+ infoPtr->bmiHeader.biXPelsPerMeter = 0;
+ infoPtr->bmiHeader.biYPelsPerMeter = 0;
+ infoPtr->bmiHeader.biClrImportant = 0;
+
+ if (usePalette) {
+ infoPtr->bmiHeader.biClrUsed = ncolors;
+ for (i = 0; i < ncolors; i++) {
+ infoPtr->bmiColors[i].rgbBlue = GetBValue(colors[i]);
+ infoPtr->bmiColors[i].rgbGreen = GetGValue(colors[i]);
+ infoPtr->bmiColors[i].rgbRed = GetRValue(colors[i]);
+ infoPtr->bmiColors[i].rgbReserved = 0;
+ }
+ } else {
+ infoPtr->bmiHeader.biClrUsed = 0;
+ }
+ bitmap = CreateDIBitmap(dc, &infoPtr->bmiHeader, CBM_INIT,
+ image->data, infoPtr, DIB_RGB_COLORS);
+ ckfree((char *) infoPtr);
+ }
+ bitmap = SelectObject(dcMem, bitmap);
+ BitBlt(dc, dest_x, dest_y, width, height, dcMem, src_x, src_y, SRCCOPY);
+ DeleteObject(SelectObject(dcMem, bitmap));
+ DeleteDC(dcMem);
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillRectangles --
+ *
+ * Fill multiple rectangular areas in the given drawable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws onto the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFillRectangles(display, d, gc, rectangles, nrectangles)
+ Display* display;
+ Drawable d;
+ GC gc;
+ XRectangle* rectangles;
+ int nrectangles;
+{
+ HDC dc;
+ int i;
+ RECT rect;
+ TkWinDCState state;
+ HBRUSH brush;
+
+ if (d == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+ brush = CreateSolidBrush(gc->foreground);
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+ TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+ HBRUSH oldBrush, stipple;
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ HBRUSH bgBrush = CreateSolidBrush(gc->background);
+
+ if (twdPtr->type != TWD_BITMAP) {
+ panic("unexpected drawable type in stipple");
+ }
+
+ /*
+ * Select stipple pattern into destination dc.
+ */
+
+ stipple = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectObject(dc, stipple);
+ dcMem = CreateCompatibleDC(dc);
+
+ /*
+ * For each rectangle, create a drawing surface which is the size of
+ * the rectangle and fill it with the background color. Then merge the
+ * result with the stipple pattern.
+ */
+
+ for (i = 0; i < nrectangles; i++) {
+ bitmap = CreateCompatibleBitmap(dc, rectangles[i].width,
+ rectangles[i].height);
+ oldBitmap = SelectObject(dcMem, bitmap);
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = rectangles[i].width;
+ rect.bottom = rectangles[i].height;
+ FillRect(dcMem, &rect, brush);
+ BitBlt(dc, rectangles[i].x, rectangles[i].y, rectangles[i].width,
+ rectangles[i].height, dcMem, 0, 0, COPYFG);
+ if (gc->fill_style == FillOpaqueStippled) {
+ FillRect(dcMem, &rect, bgBrush);
+ BitBlt(dc, rectangles[i].x, rectangles[i].y,
+ rectangles[i].width, rectangles[i].height, dcMem,
+ 0, 0, COPYBG);
+ }
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ }
+
+ DeleteDC(dcMem);
+ SelectObject(dc, oldBrush);
+ DeleteObject(stipple);
+ DeleteObject(bgBrush);
+ } else {
+ for (i = 0; i < nrectangles; i++) {
+ TkWinFillRect(dc, rectangles[i].x, rectangles[i].y,
+ rectangles[i].width, rectangles[i].height, gc->foreground);
+ }
+ }
+ DeleteObject(brush);
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RenderObject --
+ *
+ * This function draws a shape using a list of points, a
+ * stipple pattern, and the specified drawing function.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RenderObject(dc, gc, points, npoints, mode, pen, func)
+ HDC dc;
+ GC gc;
+ XPoint* points;
+ int npoints;
+ int mode;
+ HPEN pen;
+ WinDrawFunc func;
+{
+ RECT rect;
+ HPEN oldPen;
+ HBRUSH oldBrush;
+ POINT *winPoints = ConvertPoints(points, npoints, mode, &rect);
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+
+ TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+ HDC dcMem;
+ LONG width, height;
+ HBITMAP oldBitmap;
+ int i;
+ HBRUSH oldMemBrush;
+
+ if (twdPtr->type != TWD_BITMAP) {
+ panic("unexpected drawable type in stipple");
+ }
+
+ /*
+ * Grow the bounding box enough to account for wide lines.
+ */
+
+ if (gc->line_width > 1) {
+ rect.left -= gc->line_width;
+ rect.top -= gc->line_width;
+ rect.right += gc->line_width;
+ rect.bottom += gc->line_width;
+ }
+
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+
+ /*
+ * Select stipple pattern into destination dc.
+ */
+
+ SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectObject(dc, CreatePatternBrush(twdPtr->bitmap.handle));
+
+ /*
+ * Create temporary drawing surface containing a copy of the
+ * destination equal in size to the bounding box of the object.
+ */
+
+ dcMem = CreateCompatibleDC(dc);
+ oldBitmap = SelectObject(dcMem, CreateCompatibleBitmap(dc, width,
+ height));
+ oldPen = SelectObject(dcMem, pen);
+ BitBlt(dcMem, 0, 0, width, height, dc, rect.left, rect.top, SRCCOPY);
+
+ /*
+ * Translate the object for rendering in the temporary drawing
+ * surface.
+ */
+
+ for (i = 0; i < npoints; i++) {
+ winPoints[i].x -= rect.left;
+ winPoints[i].y -= rect.top;
+ }
+
+ /*
+ * Draw the object in the foreground color and copy it to the
+ * destination wherever the pattern is set.
+ */
+
+ SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE
+ : WINDING);
+ oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground));
+ (*func)(dcMem, winPoints, npoints);
+ BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG);
+
+ /*
+ * If we are rendering an opaque stipple, then draw the polygon in the
+ * background color and copy it to the destination wherever the pattern
+ * is clear.
+ */
+
+ if (gc->fill_style == FillOpaqueStippled) {
+ DeleteObject(SelectObject(dcMem,
+ CreateSolidBrush(gc->background)));
+ (*func)(dcMem, winPoints, npoints);
+ BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0,
+ COPYBG);
+ }
+
+ SelectObject(dcMem, oldPen);
+ DeleteObject(SelectObject(dcMem, oldMemBrush));
+ DeleteObject(SelectObject(dcMem, oldBitmap));
+ DeleteDC(dcMem);
+ } else {
+ oldPen = SelectObject(dc, pen);
+ oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground));
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE
+ : WINDING);
+
+ (*func)(dc, winPoints, npoints);
+
+ SelectObject(dc, oldPen);
+ }
+ DeleteObject(SelectObject(dc, oldBrush));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawLines --
+ *
+ * Draw connected lines.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Renders a series of connected lines.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XDrawLines(display, d, gc, points, npoints, mode)
+ Display* display;
+ Drawable d;
+ GC gc;
+ XPoint* points;
+ int npoints;
+ int mode;
+{
+ HPEN pen;
+ TkWinDCState state;
+ HDC dc;
+
+ if (d == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+
+ if (!tkpIsWin32s && (gc->line_width > 1)) {
+ LOGBRUSH lb;
+ DWORD style;
+
+ lb.lbStyle = BS_SOLID;
+ lb.lbColor = gc->foreground;
+ lb.lbHatch = 0;
+
+ style = PS_GEOMETRIC|PS_COSMETIC;
+ switch (gc->cap_style) {
+ case CapNotLast:
+ case CapButt:
+ style |= PS_ENDCAP_FLAT;
+ break;
+ case CapRound:
+ style |= PS_ENDCAP_ROUND;
+ break;
+ default:
+ style |= PS_ENDCAP_SQUARE;
+ break;
+ }
+ switch (gc->join_style) {
+ case JoinMiter:
+ style |= PS_JOIN_MITER;
+ break;
+ case JoinRound:
+ style |= PS_JOIN_ROUND;
+ break;
+ default:
+ style |= PS_JOIN_BEVEL;
+ break;
+ }
+ pen = ExtCreatePen(style, gc->line_width, &lb, 0, NULL);
+ } else {
+ pen = CreatePen(PS_SOLID, gc->line_width, gc->foreground);
+ }
+ RenderObject(dc, gc, points, npoints, mode, pen, Polyline);
+ DeleteObject(pen);
+
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillPolygon --
+ *
+ * Draws a filled polygon.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws a filled polygon on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFillPolygon(display, d, gc, points, npoints, shape, mode)
+ Display* display;
+ Drawable d;
+ GC gc;
+ XPoint* points;
+ int npoints;
+ int shape;
+ int mode;
+{
+ HPEN pen;
+ TkWinDCState state;
+ HDC dc;
+
+ if (d == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+
+ pen = GetStockObject(NULL_PEN);
+ RenderObject(dc, gc, points, npoints, mode, pen, Polygon);
+
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawRectangle --
+ *
+ * Draws a rectangle.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws a rectangle on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XDrawRectangle(display, d, gc, x, y, width, height)
+ Display* display;
+ Drawable d;
+ GC gc;
+ int x;
+ int y;
+ unsigned int width;
+ unsigned int height;
+{
+ HPEN pen, oldPen;
+ TkWinDCState state;
+ HBRUSH oldBrush;
+ HDC dc;
+
+ if (d == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+
+ pen = CreatePen(PS_SOLID, gc->line_width, gc->foreground);
+ oldPen = SelectObject(dc, pen);
+ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ Rectangle(dc, x, y, x+width+1, y+height+1);
+
+ DeleteObject(SelectObject(dc, oldPen));
+ SelectObject(dc, oldBrush);
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDrawArc --
+ *
+ * Draw an arc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws an arc on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XDrawArc(display, d, gc, x, y, width, height, start, extent)
+ Display* display;
+ Drawable d;
+ GC gc;
+ int x;
+ int y;
+ unsigned int width;
+ unsigned int height;
+ int start;
+ int extent;
+{
+ display->request++;
+
+ DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFillArc --
+ *
+ * Draw a filled arc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Draws a filled arc on the specified drawable.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFillArc(display, d, gc, x, y, width, height, start, extent)
+ Display* display;
+ Drawable d;
+ GC gc;
+ int x;
+ int y;
+ unsigned int width;
+ unsigned int height;
+ int start;
+ int extent;
+{
+ display->request++;
+
+ DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawOrFillArc --
+ *
+ * This procedure handles the rendering of drawn or filled
+ * arcs and chords.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Renders the requested arc.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, fill)
+ Display *display;
+ Drawable d;
+ GC gc;
+ int x, y; /* left top */
+ unsigned int width, height;
+ int start; /* start: three-o'clock (deg*64) */
+ int extent; /* extent: relative (deg*64) */
+ int fill; /* ==0 draw, !=0 fill */
+{
+ HDC dc;
+ HBRUSH brush, oldBrush;
+ HPEN pen, oldPen;
+ TkWinDCState state;
+ int clockwise = (extent < 0); /* non-zero if clockwise */
+ int xstart, ystart, xend, yend;
+ double radian_start, radian_end, xr, yr;
+
+ if (d == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, d, &state);
+
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ /*
+ * Compute the absolute starting and ending angles in normalized radians.
+ * Swap the start and end if drawing clockwise.
+ */
+
+ start = start % (64*360);
+ if (start < 0) {
+ start += (64*360);
+ }
+ extent = (start+extent) % (64*360);
+ if (extent < 0) {
+ extent += (64*360);
+ }
+ if (clockwise) {
+ int tmp = start;
+ start = extent;
+ extent = tmp;
+ }
+ radian_start = XAngleToRadians(start);
+ radian_end = XAngleToRadians(extent);
+
+ /*
+ * Now compute points on the radial lines that define the starting and
+ * ending angles. Be sure to take into account that the y-coordinate
+ * system is inverted.
+ */
+
+ xr = x + width / 2.0;
+ yr = y + height / 2.0;
+ xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5);
+ ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5);
+ xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5);
+ yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5);
+
+ /*
+ * Now draw a filled or open figure. Note that we have to
+ * increase the size of the bounding box by one to account for the
+ * difference in pixel definitions between X and Windows.
+ */
+
+ pen = CreatePen(PS_SOLID, gc->line_width, gc->foreground);
+ oldPen = SelectObject(dc, pen);
+ if (!fill) {
+ /*
+ * Note that this call will leave a gap of one pixel at the
+ * end of the arc for thin arcs. We can't use ArcTo because
+ * it's only supported under Windows NT.
+ */
+
+ Arc(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
+ } else {
+ brush = CreateSolidBrush(gc->foreground);
+ oldBrush = SelectObject(dc, brush);
+ if (gc->arc_mode == ArcChord) {
+ Chord(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
+ } else if ( gc->arc_mode == ArcPieSlice ) {
+ Pie(dc, x, y, x+width+1, y+height+1, xstart, ystart, xend, yend);
+ }
+ DeleteObject(SelectObject(dc, oldBrush));
+ }
+ DeleteObject(SelectObject(dc, oldPen));
+ TkWinReleaseDrawableDC(d, dc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkScrollWindow --
+ *
+ * Scroll a rectangle of the specified window and accumulate
+ * a damage region.
+ *
+ * Results:
+ * Returns 0 if the scroll genereated no additional damage.
+ * Otherwise, sets the region that needs to be repainted after
+ * scrolling and returns 1.
+ *
+ * Side effects:
+ * Scrolls the bits in the window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn)
+ Tk_Window tkwin; /* The window to be scrolled. */
+ GC gc; /* GC for window to be scrolled. */
+ int x, y, width, height; /* Position rectangle to be scrolled. */
+ int dx, dy; /* Distance rectangle should be moved. */
+ TkRegion damageRgn; /* Region to accumulate damage in. */
+{
+ HWND hwnd = TkWinGetHWND(Tk_WindowId(tkwin));
+ RECT scrollRect;
+
+ scrollRect.left = x;
+ scrollRect.top = y;
+ scrollRect.right = x + width;
+ scrollRect.bottom = y + height;
+ return (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn,
+ NULL, 0) == NULLREGION) ? 0 : 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinFillRect --
+ *
+ * This routine fills a rectangle with the foreground color
+ * from the specified GC ignoring all other GC values. This
+ * is the fastest way to fill a drawable with a solid color.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Modifies the contents of the DC drawing surface.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinFillRect(dc, x, y, width, height, pixel)
+ HDC dc;
+ int x, y, width, height;
+ int pixel;
+{
+ RECT rect;
+ COLORREF oldColor;
+
+ rect.left = x;
+ rect.top = y;
+ rect.right = x + width;
+ rect.bottom = y + height;
+ oldColor = SetBkColor(dc, (COLORREF)pixel);
+ SetBkMode(dc, OPAQUE);
+ ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
+ SetBkColor(dc, oldColor);
+}
diff --git a/win/tkWinEmbed.c b/win/tkWinEmbed.c
new file mode 100644
index 0000000..0dc4036
--- /dev/null
+++ b/win/tkWinEmbed.c
@@ -0,0 +1,645 @@
+/*
+ * tkWinEmbed.c --
+ *
+ * This file contains platform specific procedures for Windows platforms
+ * to provide basic operations needed for application embedding (where
+ * one application can use as its main window an internal window from
+ * another application).
+ *
+ * Copyright (c) 1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinEmbed.c 1.20 97/11/05 17:47:09;
+ */
+
+#include "tkWinInt.h"
+
+
+/*
+ * One of the following structures exists for each container in this
+ * application. It keeps track of the container window and its
+ * associated embedded window.
+ */
+
+typedef struct Container {
+ HWND parentHWnd; /* Windows HWND to the parent window */
+ TkWindow *parentPtr; /* Tk's information about the container
+ * or NULL if the container isn't
+ * in this process. */
+ HWND embeddedHWnd; /* Windows HWND to the embedded window
+ */
+ TkWindow *embeddedPtr; /* Tk's information about the embedded
+ * window, or NULL if the
+ * embedded application isn't in
+ * this process. */
+ struct Container *nextPtr; /* Next in list of all containers in
+ * this process. */
+} Container;
+
+static Container *firstContainerPtr = NULL;
+ /* First in list of all containers
+ * managed by this process. */
+
+static void CleanupContainerList _ANSI_ARGS_((
+ ClientData clientData));
+static void ContainerEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void EmbeddedEventProc _ANSI_ARGS_((
+ ClientData clientData, XEvent *eventPtr));
+static void EmbedGeometryRequest _ANSI_ARGS_((
+ Container*containerPtr, int width, int height));
+static void EmbedWindowDeleted _ANSI_ARGS_((TkWindow *winPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CleanupContainerList --
+ *
+ * Finalizes the list of containers.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Releases memory occupied by containers of embedded windows.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+CleanupContainerList(clientData)
+ ClientData clientData;
+{
+ Container *nextPtr;
+
+ for (;
+ firstContainerPtr != (Container *) NULL;
+ firstContainerPtr = nextPtr) {
+ nextPtr = firstContainerPtr->nextPtr;
+ ckfree((char *) firstContainerPtr);
+ }
+ firstContainerPtr = (Container *) NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpTestembedCmd --
+ *
+ * Test command for the embedding facility.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side effects:
+ * Currently it does not do anything.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+TkpTestembedCmd(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpUseWindow --
+ *
+ * This procedure causes a Tk window to use a given Windows handle
+ * for a window as its underlying window, rather than a new Windows
+ * window being created automatically. It is invoked by an embedded
+ * application to specify the window in which the application is
+ * embedded.
+ *
+ * Results:
+ * The return value is normally TCL_OK. If an error occurred (such as
+ * if the argument does not identify a legal Windows window handle),
+ * the return value is TCL_ERROR and an error message is left in the
+ * interp->result if interp is not NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpUseWindow(interp, tkwin, string)
+ Tcl_Interp *interp; /* If not NULL, used for error reporting
+ * if string is bogus. */
+ Tk_Window tkwin; /* Tk window that does not yet have an
+ * associated X window. */
+ char *string; /* String identifying an X window to use
+ * for tkwin; must be an integer value. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ int id;
+ HWND hwnd;
+ Container *containerPtr;
+
+ if (winPtr->window != None) {
+ panic("TkpUseWindow: Already assigned a window");
+ }
+
+ if (Tcl_GetInt(interp, string, &id) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hwnd = (HWND) id;
+
+ /*
+ * Check if the window is a valid handle. If it is invalid, return
+ * TCL_ERROR and potentially leave an error message in interp->result.
+ */
+
+ if (!IsWindow(hwnd)) {
+ if (interp != (Tcl_Interp *) NULL) {
+ Tcl_AppendResult(interp, "window \"", string,
+ "\" doesn't exist", (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+
+ /*
+ * Store the parent window in the platform private data slot so
+ * TkWmMapWindow can use it when creating the wrapper window.
+ */
+
+ winPtr->privatePtr = (struct TkWindowPrivate*) hwnd;
+
+ /*
+ * Create an event handler to clean up the Container structure when
+ * tkwin is eventually deleted.
+ */
+
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbeddedEventProc,
+ (ClientData) winPtr);
+
+ /*
+ * If this is the first container, register an exit handler so that
+ * things will get cleaned up at finalization.
+ */
+
+ if (firstContainerPtr == (Container *) NULL) {
+ Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL);
+ }
+
+ /*
+ * Save information about the container and the embedded window
+ * in a Container structure. If there is already an existing
+ * Container structure, it means that both container and embedded
+ * app. are in the same process.
+ */
+
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr->parentHWnd == hwnd) {
+ winPtr->flags |= TK_BOTH_HALVES;
+ containerPtr->parentPtr->flags |= TK_BOTH_HALVES;
+ break;
+ }
+ }
+ if (containerPtr == NULL) {
+ containerPtr = (Container *) ckalloc(sizeof(Container));
+ containerPtr->parentPtr = NULL;
+ containerPtr->parentHWnd = hwnd;
+ containerPtr->nextPtr = firstContainerPtr;
+ firstContainerPtr = containerPtr;
+ }
+
+ /*
+ * embeddedHWnd is not created yet. It will be created by TkWmMapWindow(),
+ * which will send a TK_ATTACHWINDOW to the container window.
+ * TkWinEmbeddedEventProc will process this message and set the embeddedHWnd
+ * variable
+ */
+
+ containerPtr->embeddedPtr = winPtr;
+ containerPtr->embeddedHWnd = NULL;
+
+ winPtr->flags |= TK_EMBEDDED;
+ winPtr->flags &= (~(TK_MAPPED));
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMakeContainer --
+ *
+ * This procedure is called to indicate that a particular window will
+ * be a container for an embedded application. This changes certain
+ * aspects of the window's behavior, such as whether it will receive
+ * events anymore.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMakeContainer(tkwin)
+ Tk_Window tkwin;
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ Container *containerPtr;
+
+ /*
+ * If this is the first container, register an exit handler so that
+ * things will get cleaned up at finalization.
+ */
+
+ if (firstContainerPtr == (Container *) NULL) {
+ Tcl_CreateExitHandler(CleanupContainerList, (ClientData) NULL);
+ }
+
+ /*
+ * Register the window as a container so that, for example, we can
+ * find out later if the embedded app. is in the same process.
+ */
+
+ Tk_MakeWindowExist(tkwin);
+ containerPtr = (Container *) ckalloc(sizeof(Container));
+ containerPtr->parentPtr = winPtr;
+ containerPtr->parentHWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+ containerPtr->embeddedHWnd = NULL;
+ containerPtr->embeddedPtr = NULL;
+ containerPtr->nextPtr = firstContainerPtr;
+ firstContainerPtr = containerPtr;
+ winPtr->flags |= TK_CONTAINER;
+
+ /*
+ * Unlike in tkUnixEmbed.c, we don't make any requests for events
+ * in the embedded window here. Now we just allow the embedding
+ * of another TK application into TK windows. When the embedded
+ * window makes a request, that will be done by sending to the
+ * container window a WM_USER message, which will be intercepted
+ * by TkWinContainerProc.
+ *
+ * We need to get structure events of the container itself, though.
+ */
+
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask,
+ ContainerEventProc, (ClientData) containerPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbeddedEventProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when various
+ * useful events are received for a window that is embedded in
+ * another application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Our internal state gets cleaned up when an embedded window is
+ * destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbeddedEventProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ TkWindow *winPtr = (TkWindow *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ EmbedWindowDeleted(winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinEmbeddedEventProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when
+ * various useful events are received for the *children* of a
+ * container window. It forwards relevant information, such as
+ * geometry requests, from the events into the container's
+ * application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the event. For example, when ConfigureRequest events
+ * occur, geometry information gets set for the container window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+LRESULT
+TkWinEmbeddedEventProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ Container *containerPtr;
+
+ /*
+ * Find the Container structure associated with the parent window.
+ */
+
+ for (containerPtr = firstContainerPtr;
+ containerPtr->parentHWnd != hwnd;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr == NULL) {
+ panic("TkWinContainerProc couldn't find Container record");
+ }
+ }
+
+ switch (message) {
+ case TK_ATTACHWINDOW:
+ /* An embedded window (either from this application or from
+ * another application) is trying to attach to this container.
+ * We attach it only if this container is not yet containing any
+ * window.
+ */
+ if (containerPtr->embeddedHWnd == NULL) {
+ containerPtr->embeddedHWnd = (HWND)wParam;
+ } else {
+ return 0;
+ }
+
+ break;
+ case TK_GEOMETRYREQ:
+ EmbedGeometryRequest(containerPtr, wParam, lParam);
+ break;
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedGeometryRequest --
+ *
+ * This procedure is invoked when an embedded application requests
+ * a particular size. It processes the request (which may or may
+ * not actually resize the window) and reflects the results back
+ * to the embedded application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If we deny the child's size change request, a Configure event
+ * is synthesized to let the child know that the size is the same
+ * as it used to be. Events get processed while we're waiting for
+ * the geometry managers to do their thing.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+EmbedGeometryRequest(containerPtr, width, height)
+ Container *containerPtr; /* Information about the container window. */
+ int width, height; /* Size that the child has requested. */
+{
+ TkWindow * winPtr = containerPtr->parentPtr;
+
+ /*
+ * Forward the requested size into our geometry management hierarchy
+ * via the container window. We need to send a Configure event back
+ * to the embedded application even if we decide not to resize
+ * the window; to make this happen, process all idle event handlers
+ * synchronously here (so that the geometry managers have had a
+ * chance to do whatever they want to do), and if the window's size
+ * didn't change then generate a configure event.
+ */
+ Tk_GeometryRequest((Tk_Window)winPtr, width, height);
+
+ if (containerPtr->embeddedHWnd != NULL) {
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {
+ /* Empty loop body. */
+ }
+
+ SetWindowPos(containerPtr->embeddedHWnd, NULL,
+ 0, 0, winPtr->changes.width, winPtr->changes.height, SWP_NOZORDER);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ContainerEventProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when
+ * various useful events are received for the container window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on the event. For example, when ConfigureRequest events
+ * occur, geometry information gets set for the container window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ContainerEventProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ Container *containerPtr = (Container *)clientData;
+ Tk_Window tkwin = (Tk_Window)containerPtr->parentPtr;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if (containerPtr->embeddedPtr == NULL) {
+ return;
+ }
+ /* Resize the embedded window, if there is any */
+ if (containerPtr->embeddedHWnd) {
+ SetWindowPos(containerPtr->embeddedHWnd, NULL,
+ 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), SWP_NOZORDER);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ /* The container is gone, remove it from the list */
+ EmbedWindowDeleted(containerPtr->parentPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetOtherWindow --
+ *
+ * If both the container and embedded window are in the same
+ * process, this procedure will return either one, given the other.
+ *
+ * Results:
+ * If winPtr is a container, the return value is the token for the
+ * embedded window, and vice versa. If the "other" window isn't in
+ * this process, NULL is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkWindow *
+TkpGetOtherWindow(winPtr)
+ TkWindow *winPtr; /* Tk's structure for a container or
+ * embedded window. */
+{
+ Container *containerPtr;
+
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr->embeddedPtr == winPtr) {
+ return containerPtr->parentPtr;
+ } else if (containerPtr->parentPtr == winPtr) {
+ return containerPtr->embeddedPtr;
+ }
+ }
+ panic("TkpGetOtherWindow couldn't find window");
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpClaimFocus --
+ *
+ * This procedure is invoked when someone asks or the input focus
+ * to be put on a window in an embedded application, but the
+ * application doesn't currently have the focus. It requests the
+ * input focus from the container application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The input focus may change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpClaimFocus(topLevelPtr, force)
+ TkWindow *topLevelPtr; /* Top-level window containing desired
+ * focus window; should be embedded. */
+ int force; /* One means that the container should
+ * claim the focus if it doesn't
+ * currently have it. */
+{
+ HWND hwnd = GetParent(Tk_GetHWND(topLevelPtr->window));
+ SendMessage(hwnd, TK_CLAIMFOCUS, (WPARAM) force, 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpRedirectKeyEvent --
+ *
+ * This procedure is invoked when a key press or release event
+ * arrives for an application that does not believe it owns the
+ * input focus. This can happen because of embedding; for example,
+ * X can send an event to an embedded application when the real
+ * focus window is in the container application and is an ancestor
+ * of the container. This procedure's job is to forward the event
+ * back to the application where it really belongs.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The event may get sent to a different application.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpRedirectKeyEvent(winPtr, eventPtr)
+ TkWindow *winPtr; /* Window to which the event was originally
+ * reported. */
+ XEvent *eventPtr; /* X event to redirect (should be KeyPress
+ * or KeyRelease). */
+{
+ /* not implemented */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedWindowDeleted --
+ *
+ * This procedure is invoked when a window involved in embedding
+ * (as either the container or the embedded application) is
+ * destroyed. It cleans up the Container structure for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Container structure may be freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbedWindowDeleted(winPtr)
+ TkWindow *winPtr; /* Tk's information about window that
+ * was deleted. */
+{
+ Container *containerPtr, *prevPtr;
+
+ /*
+ * Find the Container structure for this window work. Delete the
+ * information about the embedded application and free the container's
+ * record.
+ */
+
+ prevPtr = NULL;
+ containerPtr = firstContainerPtr;
+ while (1) {
+ if (containerPtr->embeddedPtr == winPtr) {
+ containerPtr->embeddedHWnd = NULL;
+ containerPtr->embeddedPtr = NULL;
+ break;
+ }
+ if (containerPtr->parentPtr == winPtr) {
+ containerPtr->parentPtr = NULL;
+ break;
+ }
+ prevPtr = containerPtr;
+ containerPtr = containerPtr->nextPtr;
+ if (containerPtr == NULL) {
+ panic("EmbedWindowDeleted couldn't find window");
+ }
+ }
+ if ((containerPtr->embeddedPtr == NULL)
+ && (containerPtr->parentPtr == NULL)) {
+ if (prevPtr == NULL) {
+ firstContainerPtr = containerPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = containerPtr->nextPtr;
+ }
+ ckfree((char *) containerPtr);
+ }
+}
diff --git a/win/tkWinFont.c b/win/tkWinFont.c
new file mode 100644
index 0000000..c1d5161
--- /dev/null
+++ b/win/tkWinFont.c
@@ -0,0 +1,643 @@
+/*
+ * tkWinFont.c --
+ *
+ * Contains the Windows implementation of the platform-independant
+ * font package interface.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinFont.c 1.20 97/05/14 15:45:30
+ */
+
+#include "tkWinInt.h"
+#include "tkFont.h"
+
+/*
+ * The following structure represents Windows' implementation of a font.
+ */
+
+typedef struct WinFont {
+ TkFont font; /* Stuff used by generic font package. Must
+ * be first in structure. */
+ HFONT hFont; /* Windows information about font. */
+ HWND hwnd; /* Toplevel window of application that owns
+ * this font, used for getting HDC. */
+ int widths[256]; /* Widths of first 256 chars in this font. */
+} WinFont;
+
+/*
+ * The following structure is used as to map between the Tcl strings
+ * that represent the system fonts and the numbers used by Windows.
+ */
+
+static TkStateMap systemMap[] = {
+ {ANSI_FIXED_FONT, "ansifixed"},
+ {ANSI_VAR_FONT, "ansi"},
+ {DEVICE_DEFAULT_FONT, "device"},
+ {OEM_FIXED_FONT, "oemfixed"},
+ {SYSTEM_FIXED_FONT, "systemfixed"},
+ {SYSTEM_FONT, "system"},
+ {-1, NULL}
+};
+
+#define ABS(x) (((x) < 0) ? -(x) : (x))
+
+static TkFont * AllocFont _ANSI_ARGS_((TkFont *tkFontPtr,
+ Tk_Window tkwin, HFONT hFont));
+static char * GetProperty _ANSI_ARGS_((CONST TkFontAttributes *faPtr,
+ CONST char *option));
+static int CALLBACK WinFontFamilyEnumProc _ANSI_ARGS_((ENUMLOGFONT *elfPtr,
+ NEWTEXTMETRIC *ntmPtr, int fontType,
+ LPARAM lParam));
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetNativeFont --
+ *
+ * Map a platform-specific native font name to a TkFont.
+ *
+ * Results:
+ * The return value is a pointer to a TkFont that represents the
+ * native font. If a native font by the given name could not be
+ * found, the return value is NULL.
+ *
+ * Every call to this procedure returns a new TkFont structure,
+ * even if the name has already been seen before. The caller should
+ * call TkpDeleteFont() when the font is no longer needed.
+ *
+ * The caller is responsible for initializing the memory associated
+ * with the generic TkFont when this function returns and releasing
+ * the contents of the generic TkFont before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+TkFont *
+TkpGetNativeFont(tkwin, name)
+ Tk_Window tkwin; /* For display where font will be used. */
+ CONST char *name; /* Platform-specific font name. */
+{
+ int object;
+ HFONT hFont;
+
+ object = TkFindStateNum(NULL, NULL, systemMap, name);
+ if (object < 0) {
+ return NULL;
+ }
+ hFont = GetStockObject(object);
+ if (hFont == NULL) {
+ panic("TkpGetNativeFont: can't allocate stock font");
+ }
+
+ return AllocFont(NULL, tkwin, hFont);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetFontFromAttributes --
+ *
+ * Given a desired set of attributes for a font, find a font with
+ * the closest matching attributes.
+ *
+ * Results:
+ * The return value is a pointer to a TkFont that represents the
+ * font with the desired attributes. If a font with the desired
+ * attributes could not be constructed, some other font will be
+ * substituted automatically. NULL is never returned.
+ *
+ * Every call to this procedure returns a new TkFont structure,
+ * even if the specified attributes have already been seen before.
+ * The caller should call TkpDeleteFont() to free the platform-
+ * specific data when the font is no longer needed.
+ *
+ * The caller is responsible for initializing the memory associated
+ * with the generic TkFont when this function returns and releasing
+ * the contents of the generic TkFont before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+TkFont *
+TkpGetFontFromAttributes(tkFontPtr, tkwin, faPtr)
+ TkFont *tkFontPtr; /* If non-NULL, store the information in
+ * this existing TkFont structure, rather than
+ * allocating a new structure to hold the
+ * font; the existing contents of the font
+ * will be released. If NULL, a new TkFont
+ * structure is allocated. */
+ Tk_Window tkwin; /* For display where font will be used. */
+ CONST TkFontAttributes *faPtr; /* Set of attributes to match. */
+{
+ LOGFONT lf;
+ HFONT hFont;
+ Window window;
+ HWND hwnd;
+ HDC hdc;
+
+ window = Tk_WindowId(((TkWindow *) tkwin)->mainPtr->winPtr);
+ hwnd = (window == None) ? NULL : TkWinGetHWND(window);
+
+ hdc = GetDC(hwnd);
+ lf.lfHeight = -faPtr->pointsize;
+ if (lf.lfHeight < 0) {
+ lf.lfHeight = MulDiv(lf.lfHeight,
+ 254 * WidthOfScreen(Tk_Screen(tkwin)),
+ 720 * WidthMMOfScreen(Tk_Screen(tkwin)));
+ }
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ lf.lfWeight = (faPtr->weight == TK_FW_NORMAL) ? FW_NORMAL : FW_BOLD;
+ lf.lfItalic = faPtr->slant;
+ lf.lfUnderline = faPtr->underline;
+ lf.lfStrikeOut = faPtr->overstrike;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfQuality = DEFAULT_QUALITY;
+ lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ if (faPtr->family == NULL) {
+ lf.lfFaceName[0] = '\0';
+ } else {
+ lstrcpyn(lf.lfFaceName, faPtr->family, sizeof(lf.lfFaceName));
+ }
+ ReleaseDC(hwnd, hdc);
+
+ /*
+ * Replace the standard X and Mac family names with the names that
+ * Windows likes.
+ */
+
+ if ((stricmp(lf.lfFaceName, "Times") == 0)
+ || (stricmp(lf.lfFaceName, "New York") == 0)) {
+ strcpy(lf.lfFaceName, "Times New Roman");
+ } else if ((stricmp(lf.lfFaceName, "Courier") == 0)
+ || (stricmp(lf.lfFaceName, "Monaco") == 0)) {
+ strcpy(lf.lfFaceName, "Courier New");
+ } else if ((stricmp(lf.lfFaceName, "Helvetica") == 0)
+ || (stricmp(lf.lfFaceName, "Geneva") == 0)) {
+ strcpy(lf.lfFaceName, "Arial");
+ }
+
+ hFont = CreateFontIndirect(&lf);
+ if (hFont == NULL) {
+ hFont = GetStockObject(SYSTEM_FONT);
+ if (hFont == NULL) {
+ panic("TkpGetFontFromAttributes: cannot get system font");
+ }
+ }
+ return AllocFont(tkFontPtr, tkwin, hFont);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpDeleteFont --
+ *
+ * Called to release a font allocated by TkpGetNativeFont() or
+ * TkpGetFontFromAttributes(). The caller should have already
+ * released the fields of the TkFont that are used exclusively by
+ * the generic TkFont code.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * TkFont is deallocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkpDeleteFont(tkFontPtr)
+ TkFont *tkFontPtr; /* Token of font to be deleted. */
+{
+ WinFont *fontPtr;
+
+ fontPtr = (WinFont *) tkFontPtr;
+ DeleteObject(fontPtr->hFont);
+ ckfree((char *) fontPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetFontFamilies, WinFontEnumFamilyProc --
+ *
+ * Return information about the font families that are available
+ * on the display of the given window.
+ *
+ * Results:
+ * interp->result is modified to hold a list of all the available
+ * font families.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkpGetFontFamilies(interp, tkwin)
+ Tcl_Interp *interp; /* Interp to hold result. */
+ Tk_Window tkwin; /* For display to query. */
+{
+ Window window;
+ HWND hwnd;
+ HDC hdc;
+
+ window = Tk_WindowId(tkwin);
+ hwnd = (window == (Window) NULL) ? NULL : TkWinGetHWND(window);
+
+ hdc = GetDC(hwnd);
+ EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontFamilyEnumProc,
+ (LPARAM) interp);
+ ReleaseDC(hwnd, hdc);
+}
+
+/* ARGSUSED */
+
+static int CALLBACK
+WinFontFamilyEnumProc(elfPtr, ntmPtr, fontType, lParam)
+ ENUMLOGFONT *elfPtr; /* Logical-font data. */
+ NEWTEXTMETRIC *ntmPtr; /* Physical-font data (not used). */
+ int fontType; /* Type of font (not used). */
+ LPARAM lParam; /* Interp to hold result. */
+{
+ Tcl_Interp *interp;
+
+ interp = (Tcl_Interp *) lParam;
+ Tcl_AppendElement(interp, elfPtr->elfLogFont.lfFaceName);
+ return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_MeasureChars --
+ *
+ * Determine the number of characters from the string that will fit
+ * in the given horizontal span. The measurement is done under the
+ * assumption that Tk_DrawChars() will be used to actually display
+ * the characters.
+ *
+ * Results:
+ * The return value is the number of characters from source that
+ * fit into the span that extends from 0 to maxLength. *lengthPtr is
+ * filled with the x-coordinate of the right edge of the last
+ * character that did fit.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+int
+Tk_MeasureChars(tkfont, source, numChars, maxLength, flags, lengthPtr)
+ Tk_Font tkfont; /* Font in which characters will be drawn. */
+ CONST char *source; /* Characters to be displayed. Need not be
+ * '\0' terminated. */
+ int numChars; /* Maximum number of characters to consider
+ * from source string. */
+ int maxLength; /* If > 0, maxLength specifies the longest
+ * permissible line length; don't consider any
+ * character that would cross this
+ * x-position. If <= 0, then line length is
+ * unbounded and the flags argument is
+ * ignored. */
+ int flags; /* Various flag bits OR-ed together:
+ * TK_PARTIAL_OK means include the last char
+ * which only partially fit on this line.
+ * TK_WHOLE_WORDS means stop on a word
+ * boundary, if possible.
+ * TK_AT_LEAST_ONE means return at least one
+ * character even if no characters fit. */
+ int *lengthPtr; /* Filled with x-location just after the
+ * terminating character. */
+{
+ WinFont *fontPtr;
+ HDC hdc;
+ HFONT hFont;
+ int curX, curIdx;
+
+ /*
+ * On the authority of the Gates Empire, Windows does not use kerning
+ * or fractional character widths when displaying text on the screen.
+ * So that means we can safely measure individual characters or spans
+ * of characters and add up the widths w/o any "off-by-one pixel"
+ * errors.
+ */
+
+ fontPtr = (WinFont *) tkfont;
+
+ hdc = GetDC(fontPtr->hwnd);
+ hFont = SelectObject(hdc, fontPtr->hFont);
+
+ if (numChars == 0) {
+ curX = 0;
+ curIdx = 0;
+ } else if (maxLength <= 0) {
+ SIZE size;
+
+ GetTextExtentPoint(hdc, source, numChars, &size);
+ curX = size.cx;
+ curIdx = numChars;
+ } else {
+ int newX, termX, sawNonSpace;
+ CONST char *term, *end, *p;
+ int ch;
+
+ ch = UCHAR(*source);
+ newX = curX = termX = 0;
+
+ term = source;
+ end = source + numChars;
+
+ sawNonSpace = !isspace(ch);
+ for (p = source; ; ) {
+ newX += fontPtr->widths[ch];
+ if (newX > maxLength) {
+ break;
+ }
+ curX = newX;
+ p++;
+ if (p >= end) {
+ term = end;
+ termX = curX;
+ break;
+ }
+
+ ch = UCHAR(*p);
+ if (isspace(ch)) {
+ if (sawNonSpace) {
+ term = p;
+ termX = curX;
+ sawNonSpace = 0;
+ }
+ } else {
+ sawNonSpace = 1;
+ }
+ }
+
+ /*
+ * P points to the first character that doesn't fit in the desired
+ * span. Use the flags to figure out what to return.
+ */
+
+ if ((flags & TK_PARTIAL_OK) && (p < end) && (curX < maxLength)) {
+ /*
+ * Include the first character that didn't quite fit in the desired
+ * span. The width returned will include the width of that extra
+ * character.
+ */
+
+ curX = newX;
+ p++;
+ }
+ if ((flags & TK_AT_LEAST_ONE) && (term == source) && (p < end)) {
+ term = p;
+ termX = curX;
+ if (term == source) {
+ term++;
+ termX = newX;
+ }
+ } else if ((p >= end) || !(flags & TK_WHOLE_WORDS)) {
+ term = p;
+ termX = curX;
+ }
+
+ curX = termX;
+ curIdx = term - source;
+ }
+
+ SelectObject(hdc, hFont);
+ ReleaseDC(fontPtr->hwnd, hdc);
+
+ *lengthPtr = curX;
+ return curIdx;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_DrawChars --
+ *
+ * Draw a string of characters on the screen.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+Tk_DrawChars(display, drawable, gc, tkfont, source, numChars, x, y)
+ Display *display; /* Display on which to draw. */
+ Drawable drawable; /* Window or pixmap in which to draw. */
+ GC gc; /* Graphics context for drawing characters. */
+ Tk_Font tkfont; /* Font in which characters will be drawn;
+ * must be the same as font used in GC. */
+ CONST char *source; /* Characters to be displayed. Need not be
+ * '\0' terminated. All Tk meta-characters
+ * (tabs, control characters, and newlines)
+ * should be stripped out of the string that
+ * is passed to this function. If they are
+ * not stripped out, they will be displayed as
+ * regular printing characters. */
+ int numChars; /* Number of characters in string. */
+ int x, y; /* Coordinates at which to place origin of
+ * string when drawing. */
+{
+ HDC dc;
+ HFONT hFont;
+ TkWinDCState state;
+ WinFont *fontPtr;
+
+ fontPtr = (WinFont *) gc->font;
+ display->request++;
+
+ if (drawable == None) {
+ return;
+ }
+
+ dc = TkWinGetDrawableDC(display, drawable, &state);
+
+ SetROP2(dc, tkpWinRopModes[gc->function]);
+
+ if ((gc->fill_style == FillStippled
+ || gc->fill_style == FillOpaqueStippled)
+ && gc->stipple != None) {
+ TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple;
+ HBRUSH oldBrush, stipple;
+ HBITMAP oldBitmap, bitmap;
+ HDC dcMem;
+ TEXTMETRIC tm;
+ SIZE size;
+
+ if (twdPtr->type != TWD_BITMAP) {
+ panic("unexpected drawable type in stipple");
+ }
+
+ /*
+ * Select stipple pattern into destination dc.
+ */
+
+ dcMem = CreateCompatibleDC(dc);
+
+ stipple = CreatePatternBrush(twdPtr->bitmap.handle);
+ SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL);
+ oldBrush = SelectObject(dc, stipple);
+
+ SetTextAlign(dcMem, TA_LEFT | TA_TOP);
+ SetTextColor(dcMem, gc->foreground);
+ SetBkMode(dcMem, TRANSPARENT);
+ SetBkColor(dcMem, RGB(0, 0, 0));
+
+ hFont = SelectObject(dcMem, fontPtr->hFont);
+
+ /*
+ * Compute the bounding box and create a compatible bitmap.
+ */
+
+ GetTextExtentPoint(dcMem, source, numChars, &size);
+ GetTextMetrics(dcMem, &tm);
+ size.cx -= tm.tmOverhang;
+ bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy);
+ oldBitmap = SelectObject(dcMem, bitmap);
+
+ /*
+ * The following code is tricky because fonts are rendered in multiple
+ * colors. First we draw onto a black background and copy the white
+ * bits. Then we draw onto a white background and copy the black bits.
+ * Both the foreground and background bits of the font are ANDed with
+ * the stipple pattern as they are copied.
+ */
+
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, BLACKNESS);
+ TextOut(dcMem, 0, 0, source, numChars);
+ BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0xEA02E9);
+ PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS);
+ TextOut(dcMem, 0, 0, source, numChars);
+ BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem,
+ 0, 0, 0x8A0E06);
+
+ /*
+ * Destroy the temporary bitmap and restore the device context.
+ */
+
+ SelectObject(dcMem, hFont);
+ SelectObject(dcMem, oldBitmap);
+ DeleteObject(bitmap);
+ DeleteDC(dcMem);
+ SelectObject(dc, oldBrush);
+ DeleteObject(stipple);
+ } else {
+ SetTextAlign(dc, TA_LEFT | TA_BASELINE);
+ SetTextColor(dc, gc->foreground);
+ SetBkMode(dc, TRANSPARENT);
+ hFont = SelectObject(dc, fontPtr->hFont);
+ TextOut(dc, x, y, source, numChars);
+ SelectObject(dc, hFont);
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AllocFont --
+ *
+ * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
+ * Allocates and intializes the memory for a new TkFont that
+ * wraps the platform-specific data.
+ *
+ * Results:
+ * Returns pointer to newly constructed TkFont.
+ *
+ * The caller is responsible for initializing the fields of the
+ * TkFont that are used exclusively by the generic TkFont code, and
+ * for releasing those fields before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static TkFont *
+AllocFont(tkFontPtr, tkwin, hFont)
+ TkFont *tkFontPtr; /* If non-NULL, store the information in
+ * this existing TkFont structure, rather than
+ * allocating a new structure to hold the
+ * font; the existing contents of the font
+ * will be released. If NULL, a new TkFont
+ * structure is allocated. */
+ Tk_Window tkwin; /* For display where font will be used. */
+ HFONT hFont; /* Windows information about font. */
+{
+ HWND hwnd;
+ WinFont *fontPtr;
+ HDC hdc;
+ TEXTMETRIC tm;
+ Window window;
+ char buf[LF_FACESIZE];
+ TkFontAttributes *faPtr;
+
+ if (tkFontPtr != NULL) {
+ fontPtr = (WinFont *) tkFontPtr;
+ DeleteObject(fontPtr->hFont);
+ } else {
+ fontPtr = (WinFont *) ckalloc(sizeof(WinFont));
+ }
+
+ window = Tk_WindowId(((TkWindow *) tkwin)->mainPtr->winPtr);
+ hwnd = (window == None) ? NULL : TkWinGetHWND(window);
+
+ hdc = GetDC(hwnd);
+ hFont = SelectObject(hdc, hFont);
+ GetTextFace(hdc, sizeof(buf), buf);
+ GetTextMetrics(hdc, &tm);
+ GetCharWidth(hdc, 0, 255, fontPtr->widths);
+
+ fontPtr->font.fid = (Font) fontPtr;
+
+ faPtr = &fontPtr->font.fa;
+ faPtr->family = Tk_GetUid(buf);
+ faPtr->pointsize = MulDiv(tm.tmHeight - tm.tmInternalLeading,
+ 720 * WidthMMOfScreen(Tk_Screen(tkwin)),
+ 254 * WidthOfScreen(Tk_Screen(tkwin)));
+ faPtr->weight = (tm.tmWeight > FW_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL;
+ faPtr->slant = (tm.tmItalic != 0) ? TK_FS_ITALIC : TK_FS_ROMAN;
+ faPtr->underline = (tm.tmUnderlined != 0) ? 1 : 0;
+ faPtr->overstrike = (tm.tmStruckOut != 0) ? 1 : 0;
+
+ fontPtr->font.fm.ascent = tm.tmAscent;
+ fontPtr->font.fm.descent = tm.tmDescent;
+ fontPtr->font.fm.maxWidth = tm.tmMaxCharWidth;
+ fontPtr->font.fm.fixed = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
+
+ hFont = SelectObject(hdc, hFont);
+ ReleaseDC(hwnd, hdc);
+
+ fontPtr->hFont = hFont;
+ fontPtr->hwnd = hwnd;
+
+ return (TkFont *) fontPtr;
+}
+
diff --git a/win/tkWinImage.c b/win/tkWinImage.c
new file mode 100644
index 0000000..388a58a
--- /dev/null
+++ b/win/tkWinImage.c
@@ -0,0 +1,329 @@
+/*
+ * tkWinImage.c --
+ *
+ * This file contains routines for manipulation full-color images.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinImage.c 1.13 97/07/07 11:19:45
+ */
+
+#include "tkWinInt.h"
+
+static int DestroyImage _ANSI_ARGS_((XImage* data));
+static unsigned long ImageGetPixel _ANSI_ARGS_((XImage *image, int x, int y));
+static int PutPixel _ANSI_ARGS_((XImage *image, int x, int y,
+ unsigned long pixel));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DestroyImage --
+ *
+ * This is a trivial wrapper around ckfree to make it possible to
+ * pass ckfree as a pointer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Deallocates the image.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+DestroyImage(imagePtr)
+ XImage *imagePtr; /* image to free */
+{
+ if (imagePtr) {
+ if (imagePtr->data) {
+ ckfree((char*)imagePtr->data);
+ }
+ ckfree((char*)imagePtr);
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ImageGetPixel --
+ *
+ * Get a single pixel from an image.
+ *
+ * Results:
+ * Returns the 32 bit pixel value.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+unsigned long
+ImageGetPixel(image, x, y)
+ XImage *image;
+ int x, y;
+{
+ unsigned long pixel = 0;
+ unsigned char *srcPtr = &(image->data[(y * image->bytes_per_line)
+ + ((x * image->bits_per_pixel) / NBBY)]);
+
+ switch (image->bits_per_pixel) {
+ case 32:
+ case 24:
+ pixel = RGB(srcPtr[2], srcPtr[1], srcPtr[0]);
+ break;
+ case 16:
+ pixel = RGB(((((WORD*)srcPtr)[0]) >> 7) & 0xf8,
+ ((((WORD*)srcPtr)[0]) >> 2) & 0xf8,
+ ((((WORD*)srcPtr)[0]) << 3) & 0xf8);
+ break;
+ case 8:
+ pixel = srcPtr[0];
+ break;
+ case 4:
+ pixel = ((x%2) ? (*srcPtr) : ((*srcPtr) >> 4)) & 0x0f;
+ break;
+ case 1:
+ pixel = ((*srcPtr) & (0x80 >> (x%8))) ? 1 : 0;
+ break;
+ }
+ return pixel;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * PutPixel --
+ *
+ * Set a single pixel in an image.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+PutPixel(image, x, y, pixel)
+ XImage *image;
+ int x, y;
+ unsigned long pixel;
+{
+ unsigned char *destPtr = &(image->data[(y * image->bytes_per_line)
+ + ((x * image->bits_per_pixel) / NBBY)]);
+
+ switch (image->bits_per_pixel) {
+ case 32:
+ /*
+ * Pixel is DWORD: 0x00BBGGRR
+ */
+
+ destPtr[3] = 0;
+ case 24:
+ /*
+ * Pixel is triplet: 0xBBGGRR.
+ */
+
+ destPtr[0] = (unsigned char) GetBValue(pixel);
+ destPtr[1] = (unsigned char) GetGValue(pixel);
+ destPtr[2] = (unsigned char) GetRValue(pixel);
+ break;
+ case 16:
+ /*
+ * Pixel is WORD: 5-5-5 (R-G-B)
+ */
+
+ (*(WORD*)destPtr) =
+ ((GetRValue(pixel) & 0xf8) << 7)
+ | ((GetGValue(pixel) & 0xf8) <<2)
+ | ((GetBValue(pixel) & 0xf8) >> 3);
+ break;
+ case 8:
+ /*
+ * Pixel is 8-bit index into color table.
+ */
+
+ (*destPtr) = (unsigned char) pixel;
+ break;
+ case 4:
+ /*
+ * Pixel is 4-bit index in MSBFirst order.
+ */
+ if (x%2) {
+ (*destPtr) = (unsigned char) (((*destPtr) & 0xf0)
+ | (pixel & 0x0f));
+ } else {
+ (*destPtr) = (unsigned char) (((*destPtr) & 0x0f)
+ | ((pixel << 4) & 0xf0));
+ }
+ break;
+ case 1: {
+ /*
+ * Pixel is bit in MSBFirst order.
+ */
+
+ int mask = (0x80 >> (x%8));
+ if (pixel) {
+ (*destPtr) |= mask;
+ } else {
+ (*destPtr) &= ~mask;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XCreateImage --
+ *
+ * Allocates storage for a new XImage.
+ *
+ * Results:
+ * Returns a newly allocated XImage.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+XImage *
+XCreateImage(display, visual, depth, format, offset, data, width, height,
+ bitmap_pad, bytes_per_line)
+ Display* display;
+ Visual* visual;
+ unsigned int depth;
+ int format;
+ int offset;
+ char* data;
+ unsigned int width;
+ unsigned int height;
+ int bitmap_pad;
+ int bytes_per_line;
+{
+ XImage* imagePtr = (XImage *) ckalloc(sizeof(XImage));
+ imagePtr->width = width;
+ imagePtr->height = height;
+ imagePtr->xoffset = offset;
+ imagePtr->format = format;
+ imagePtr->data = data;
+ imagePtr->byte_order = LSBFirst;
+ imagePtr->bitmap_unit = 8;
+ imagePtr->bitmap_bit_order = MSBFirst;
+ imagePtr->bitmap_pad = bitmap_pad;
+ imagePtr->bits_per_pixel = depth;
+ imagePtr->depth = depth;
+
+ /*
+ * Under Windows, bitmap_pad must be on an LONG data-type boundary.
+ */
+
+#define LONGBITS (sizeof(LONG) * 8)
+
+ bitmap_pad = (bitmap_pad + LONGBITS - 1) / LONGBITS * LONGBITS;
+
+ /*
+ * Round to the nearest bitmap_pad boundary.
+ */
+
+ if (bytes_per_line) {
+ imagePtr->bytes_per_line = bytes_per_line;
+ } else {
+ imagePtr->bytes_per_line = (((depth * width)
+ + (bitmap_pad - 1)) >> 3) & ~((bitmap_pad >> 3) - 1);
+ }
+
+ imagePtr->red_mask = 0;
+ imagePtr->green_mask = 0;
+ imagePtr->blue_mask = 0;
+
+ imagePtr->f.put_pixel = PutPixel;
+ imagePtr->f.get_pixel = ImageGetPixel;
+ imagePtr->f.destroy_image = DestroyImage;
+ imagePtr->f.create_image = NULL;
+ imagePtr->f.sub_image = NULL;
+ imagePtr->f.add_pixel = NULL;
+
+ return imagePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGetImage --
+ *
+ * This function copies data from a pixmap or window into an
+ * XImage.
+ *
+ * Results:
+ * Returns a newly allocated image containing the data from the
+ * given rectangle of the given drawable.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+XImage *
+XGetImage(display, d, x, y, width, height, plane_mask, format)
+ Display* display;
+ Drawable d;
+ int x;
+ int y;
+ unsigned int width;
+ unsigned int height;
+ unsigned long plane_mask;
+ int format;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+ XImage *imagePtr;
+ HDC dc;
+ char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)];
+ BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf;
+
+ if ((twdPtr->type != TWD_BITMAP) || (twdPtr->bitmap.handle == NULL)
+ || (format != XYPixmap) || (plane_mask != 1)) {
+ panic("XGetImage: not implemented");
+ }
+
+
+ imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL,
+ width, height, 32, 0);
+ imagePtr->data = ckalloc(imagePtr->bytes_per_line * imagePtr->height);
+
+ dc = GetDC(NULL);
+
+ GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL,
+ infoPtr, DIB_RGB_COLORS);
+
+ infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ infoPtr->bmiHeader.biWidth = width;
+ infoPtr->bmiHeader.biHeight = -(LONG)height;
+ infoPtr->bmiHeader.biPlanes = 1;
+ infoPtr->bmiHeader.biBitCount = 1;
+ infoPtr->bmiHeader.biCompression = BI_RGB;
+ infoPtr->bmiHeader.biCompression = 0;
+ infoPtr->bmiHeader.biXPelsPerMeter = 0;
+ infoPtr->bmiHeader.biYPelsPerMeter = 0;
+ infoPtr->bmiHeader.biClrUsed = 0;
+ infoPtr->bmiHeader.biClrImportant = 0;
+
+ GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data,
+ infoPtr, DIB_RGB_COLORS);
+ ReleaseDC(NULL, dc);
+
+ return imagePtr;
+}
diff --git a/win/tkWinInit.c b/win/tkWinInit.c
new file mode 100644
index 0000000..400f693
--- /dev/null
+++ b/win/tkWinInit.c
@@ -0,0 +1,121 @@
+/*
+ * tkWinInit.c --
+ *
+ * This file contains Windows-specific interpreter initialization
+ * functions.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinInit.c 1.29 97/07/24 14:46:35
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * The Init script (common to Windows and Unix platforms) is
+ * defined in tkInitScript.h
+ */
+#include "tkInitScript.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpInit --
+ *
+ * Performs Windows-specific interpreter initialization related to the
+ * tk_library variable.
+ *
+ * Results:
+ * A standard Tcl completion code (TCL_OK or TCL_ERROR). Also
+ * leaves information in interp->result.
+ *
+ * Side effects:
+ * Sets "tk_library" Tcl variable, runs "tk.tcl" script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpInit(interp)
+ Tcl_Interp *interp;
+{
+ return Tcl_Eval(interp, initScript);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetAppName --
+ *
+ * Retrieves the name of the current application from a platform
+ * specific location. For Windows, the application name is the
+ * root of the tail of the path contained in the tcl variable argv0.
+ *
+ * Results:
+ * Returns the application name in the given Tcl_DString.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpGetAppName(interp, namePtr)
+ Tcl_Interp *interp;
+ Tcl_DString *namePtr; /* A previously initialized Tcl_DString. */
+{
+ int argc;
+ char **argv = NULL, *name, *p;
+
+ name = Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY);
+ if (name != NULL) {
+ Tcl_SplitPath(name, &argc, &argv);
+ if (argc > 0) {
+ name = argv[argc-1];
+ p = strrchr(name, '.');
+ if (p != NULL) {
+ *p = '\0';
+ }
+ } else {
+ name = NULL;
+ }
+ }
+ if ((name == NULL) || (*name == 0)) {
+ name = "tk";
+ }
+ Tcl_DStringAppend(namePtr, name, -1);
+ if (argv != NULL) {
+ ckfree((char *)argv);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayWarning --
+ *
+ * This routines is called from Tk_Main to display warning
+ * messages that occur during startup.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Displays a message box.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayWarning(msg, title)
+ char *msg; /* Message to be displayed. */
+ char *title; /* Title of warning. */
+{
+ MessageBox(NULL, msg, title, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL
+ | MB_SETFOREGROUND | MB_TOPMOST);
+}
diff --git a/win/tkWinInt.h b/win/tkWinInt.h
new file mode 100644
index 0000000..f3bca19
--- /dev/null
+++ b/win/tkWinInt.h
@@ -0,0 +1,194 @@
+/*
+ * tkWinInt.h --
+ *
+ * This file contains declarations that are shared among the
+ * Windows-specific parts of Tk, but aren't used by the rest of
+ * Tk.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinInt.h 1.34 97/09/02 13:06:20
+ */
+
+#ifndef _TKWININT
+#define _TKWININT
+
+#ifndef _TKINT
+#include "tkInt.h"
+#endif
+
+/*
+ * Include platform specific public interfaces.
+ */
+
+#ifndef _TKWIN
+#include "tkWin.h"
+#endif
+
+/*
+ * Define constants missing from older Win32 SDK header files.
+ */
+
+#ifndef WS_EX_TOOLWINDOW
+#define WS_EX_TOOLWINDOW 0x00000080L
+#endif
+
+typedef struct TkFontAttributes TkFontAttributes;
+
+/*
+ * The TkWinDCState is used to save the state of a device context
+ * so that it can be restored later.
+ */
+
+typedef struct TkWinDCState {
+ HPALETTE palette;
+} TkWinDCState;
+
+/*
+ * The TkWinDrawable is the internal implementation of an X Drawable (either
+ * a Window or a Pixmap). The following constants define the valid Drawable
+ * types.
+ */
+
+#define TWD_BITMAP 1
+#define TWD_WINDOW 2
+#define TWD_WINDC 3
+
+typedef struct {
+ int type;
+ HWND handle;
+ TkWindow *winPtr;
+} TkWinWindow;
+
+typedef struct {
+ int type;
+ HBITMAP handle;
+ Colormap colormap;
+ int depth;
+} TkWinBitmap;
+
+typedef struct {
+ int type;
+ HDC hdc;
+}TkWinDC;
+
+typedef union {
+ int type;
+ TkWinWindow window;
+ TkWinBitmap bitmap;
+ TkWinDC winDC;
+} TkWinDrawable;
+
+/*
+ * The following macros are used to retrieve internal values from a Drawable.
+ */
+
+#define TkWinGetHWND(w) (((TkWinDrawable *) w)->window.handle)
+#define TkWinGetWinPtr(w) (((TkWinDrawable*)w)->window.winPtr)
+#define TkWinGetHBITMAP(w) (((TkWinDrawable*)w)->bitmap.handle)
+#define TkWinGetColormap(w) (((TkWinDrawable*)w)->bitmap.colormap)
+#define TkWinGetHDC(w) (((TkWinDrawable *) w)->winDC.hdc)
+
+/*
+ * The following structure is used to encapsulate palette information.
+ */
+
+typedef struct {
+ HPALETTE palette; /* Palette handle used when drawing. */
+ UINT size; /* Number of entries in the palette. */
+ int stale; /* 1 if palette needs to be realized,
+ * otherwise 0. If the palette is stale,
+ * then an idle handler is scheduled to
+ * realize the palette. */
+ Tcl_HashTable refCounts; /* Hash table of palette entry reference counts
+ * indexed by pixel value. */
+} TkWinColormap;
+
+/*
+ * The following macro retrieves the Win32 palette from a colormap.
+ */
+
+#define TkWinGetPalette(colormap) (((TkWinColormap *) colormap)->palette)
+
+/*
+ * The following macros define the class names for Tk Window types.
+ */
+
+#define TK_WIN_TOPLEVEL_CLASS_NAME "TkTopLevel"
+#define TK_WIN_CHILD_CLASS_NAME "TkChild"
+
+/*
+ * The following variable indicates whether we are restricted to Win32s
+ * GDI calls.
+ */
+
+extern int tkpIsWin32s;
+
+/*
+ * The following variable is a translation table between X gc functions and
+ * Win32 raster op modes.
+ */
+
+extern int tkpWinRopModes[];
+
+/*
+ * The following defines are used with TkWinGetBorderPixels to get the
+ * extra 2 border colors from a Tk_3DBorder.
+ */
+
+#define TK_3D_LIGHT2 TK_3D_DARK_GC+1
+#define TK_3D_DARK2 TK_3D_DARK_GC+2
+
+/*
+ * Internal procedures used by more than one source file.
+ */
+
+extern LRESULT CALLBACK TkWinChildProc _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+extern void TkWinClipboardRender _ANSI_ARGS_((TkDisplay *dispPtr,
+ UINT format));
+extern LRESULT TkWinEmbeddedEventProc _ANSI_ARGS_((HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam));
+extern void TkWinFillRect _ANSI_ARGS_((HDC dc, int x, int y,
+ int width, int height, int pixel));
+extern COLORREF TkWinGetBorderPixels _ANSI_ARGS_((Tk_Window tkwin,
+ Tk_3DBorder border, int which));
+extern HDC TkWinGetDrawableDC _ANSI_ARGS_((Display *display,
+ Drawable d, TkWinDCState* state));
+extern int TkWinGetModifierState _ANSI_ARGS_((void));
+extern HPALETTE TkWinGetSystemPalette _ANSI_ARGS_((void));
+extern HWND TkWinGetWrapperWindow _ANSI_ARGS_((Tk_Window tkwin));
+extern int TkWinHandleMenuEvent _ANSI_ARGS_((HWND *phwnd,
+ UINT *pMessage, WPARAM *pwParam, LPARAM *plParam,
+ LRESULT *plResult));
+extern int TkWinIndexOfColor _ANSI_ARGS_((XColor *colorPtr));
+extern void TkWinPointerDeadWindow _ANSI_ARGS_((TkWindow *winPtr));
+extern void TkWinPointerEvent _ANSI_ARGS_((HWND hwnd, int x,
+ int y));
+extern void TkWinPointerInit _ANSI_ARGS_((void));
+extern LRESULT TkWinReflectMessage _ANSI_ARGS_((HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam));
+extern void TkWinReleaseDrawableDC _ANSI_ARGS_((Drawable d,
+ HDC hdc, TkWinDCState* state));
+extern LRESULT TkWinResendEvent _ANSI_ARGS_((WNDPROC wndproc,
+ HWND hwnd, XEvent *eventPtr));
+extern HPALETTE TkWinSelectPalette _ANSI_ARGS_((HDC dc,
+ Colormap colormap));
+extern void TkWinSetMenu _ANSI_ARGS_((Tk_Window tkwin,
+ HMENU hMenu));
+extern void TkWinSetWindowPos _ANSI_ARGS_((HWND hwnd,
+ HWND siblingHwnd, int pos));
+extern void TkWinUpdateCursor _ANSI_ARGS_((TkWindow *winPtr));
+extern void TkWinWmCleanup _ANSI_ARGS_((HINSTANCE hInstance));
+extern HWND TkWinWmFindEmbedAssociation _ANSI_ARGS_((
+ TkWindow *winPtr));
+extern void TkWinWmStoreEmbedAssociation _ANSI_ARGS_((
+ TkWindow *winPtr, HWND hwnd));
+extern void TkWinXCleanup _ANSI_ARGS_((HINSTANCE hInstance));
+extern void TkWinXInit _ANSI_ARGS_((HINSTANCE hInstance));
+
+#endif /* _TKWININT */
+
diff --git a/win/tkWinKey.c b/win/tkWinKey.c
new file mode 100644
index 0000000..3589143
--- /dev/null
+++ b/win/tkWinKey.c
@@ -0,0 +1,360 @@
+/*
+ * tkWinKey.c --
+ *
+ * This file contains X emulation routines for keyboard related
+ * functions.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinKey.c 1.9 97/06/20 15:12:39
+ */
+
+#include "tkWinInt.h"
+
+typedef struct {
+ unsigned int keycode;
+ KeySym keysym;
+} Keys;
+
+static Keys keymap[] = {
+ VK_CANCEL, XK_Cancel,
+ VK_BACK, XK_BackSpace,
+ VK_TAB, XK_Tab,
+ VK_CLEAR, XK_Clear,
+ VK_RETURN, XK_Return,
+ VK_SHIFT, XK_Shift_L,
+ VK_CONTROL, XK_Control_L,
+ VK_MENU, XK_Alt_L,
+ VK_PAUSE, XK_Pause,
+ VK_CAPITAL, XK_Caps_Lock,
+ VK_ESCAPE, XK_Escape,
+ VK_SPACE, XK_space,
+ VK_PRIOR, XK_Prior,
+ VK_NEXT, XK_Next,
+ VK_END, XK_End,
+ VK_HOME, XK_Home,
+ VK_LEFT, XK_Left,
+ VK_UP, XK_Up,
+ VK_RIGHT, XK_Right,
+ VK_DOWN, XK_Down,
+ VK_SELECT, XK_Select,
+ VK_PRINT, XK_Print,
+ VK_EXECUTE, XK_Execute,
+ VK_INSERT, XK_Insert,
+ VK_DELETE, XK_Delete,
+ VK_HELP, XK_Help,
+ VK_F1, XK_F1,
+ VK_F2, XK_F2,
+ VK_F3, XK_F3,
+ VK_F4, XK_F4,
+ VK_F5, XK_F5,
+ VK_F6, XK_F6,
+ VK_F7, XK_F7,
+ VK_F8, XK_F8,
+ VK_F9, XK_F9,
+ VK_F10, XK_F10,
+ VK_F11, XK_F11,
+ VK_F12, XK_F12,
+ VK_F13, XK_F13,
+ VK_F14, XK_F14,
+ VK_F15, XK_F15,
+ VK_F16, XK_F16,
+ VK_F17, XK_F17,
+ VK_F18, XK_F18,
+ VK_F19, XK_F19,
+ VK_F20, XK_F20,
+ VK_F21, XK_F21,
+ VK_F22, XK_F22,
+ VK_F23, XK_F23,
+ VK_F24, XK_F24,
+ VK_NUMLOCK, XK_Num_Lock,
+ VK_SCROLL, XK_Scroll_Lock,
+ 0, NoSymbol
+};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XLookupString --
+ *
+ * Retrieve the string equivalent for the given keyboard event.
+ *
+ * Results:
+ * Returns the number of characters stored in buffer_return.
+ *
+ * Side effects:
+ * Retrieves the characters stored in the event and inserts them
+ * into buffer_return.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return,
+ status_in_out)
+ XKeyEvent* event_struct;
+ char* buffer_return;
+ int bytes_buffer;
+ KeySym* keysym_return;
+ XComposeStatus* status_in_out;
+{
+ int i, limit;
+
+ if (event_struct->send_event != -1) {
+ /*
+ * This is an event generated from generic code. It has no
+ * nchars or trans_chars members.
+ */
+
+ int index;
+ KeySym keysym;
+
+ index = 0;
+ if (event_struct->state & ShiftMask) {
+ index |= 1;
+ }
+ if (event_struct->state & Mod1Mask) {
+ index |= 2;
+ }
+ keysym = XKeycodeToKeysym(event_struct->display,
+ event_struct->keycode, index);
+ if (((keysym != NoSymbol) && (keysym > 0) && (keysym < 256))
+ || (keysym == XK_Return)
+ || (keysym == XK_Tab)) {
+ buffer_return[0] = (char) keysym;
+ return 1;
+ }
+ return 0;
+ }
+ if ((event_struct->nchars <= 0) || (buffer_return == NULL)) {
+ return 0;
+ }
+ limit = (event_struct->nchars < bytes_buffer) ? event_struct->nchars :
+ bytes_buffer;
+
+ for (i = 0; i < limit; i++) {
+ buffer_return[i] = event_struct->trans_chars[i];
+ }
+
+ if (keysym_return != NULL) {
+ *keysym_return = NoSymbol;
+ }
+ return i;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeycodeToKeysym --
+ *
+ * Translate from a system-dependent keycode to a
+ * system-independent keysym.
+ *
+ * Results:
+ * Returns the translated keysym, or NoSymbol on failure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeySym
+XKeycodeToKeysym(display, keycode, index)
+ Display* display;
+ unsigned int keycode;
+ int index;
+{
+ Keys* key;
+ BYTE keys[256];
+ int result;
+ char buf[4];
+ unsigned int scancode = MapVirtualKey(keycode, 0);
+
+ memset(keys, 0, 256);
+ if (index & 0x02) {
+ keys[VK_NUMLOCK] = 1;
+ }
+ if (index & 0x01) {
+ keys[VK_SHIFT] = 0x80;
+ }
+ result = ToAscii(keycode, scancode, keys, (LPWORD) buf, 0);
+
+ /*
+ * Keycode mapped to a valid Latin-1 character. Since the keysyms
+ * for alphanumeric characters map onto Latin-1, we just return it.
+ */
+
+ if (result == 1 && buf[0] >= 0x20) {
+ return (KeySym) buf[0];
+ }
+
+ /*
+ * Keycode is a non-alphanumeric key, so we have to do the lookup.
+ */
+
+ for (key = keymap; key->keycode != 0; key++) {
+ if (key->keycode == keycode) {
+ return key->keysym;
+ }
+ }
+
+ return NoSymbol;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeysymToKeycode --
+ *
+ * Translate a keysym back into a keycode.
+ *
+ * Results:
+ * Returns the keycode that would generate the specified keysym.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeyCode
+XKeysymToKeycode(display, keysym)
+ Display* display;
+ KeySym keysym;
+{
+ Keys* key;
+ SHORT result;
+
+ if (keysym >= 0x20) {
+ result = VkKeyScan((char) keysym);
+ if (result != -1) {
+ return (KeyCode) (result & 0xff);
+ }
+ }
+
+ /*
+ * Couldn't map the character to a virtual keycode, so do a
+ * table lookup.
+ */
+
+ for (key = keymap; key->keycode != 0; key++) {
+ if (key->keysym == keysym) {
+ return key->keycode;
+ }
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGetModifierMapping --
+ *
+ * Fetch the current keycodes used as modifiers.
+ *
+ * Results:
+ * Returns a new modifier map.
+ *
+ * Side effects:
+ * Allocates a new modifier map data structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+XModifierKeymap *
+XGetModifierMapping(display)
+ Display* display;
+{
+ XModifierKeymap *map = (XModifierKeymap *)ckalloc(sizeof(XModifierKeymap));
+
+ map->max_keypermod = 1;
+ map->modifiermap = (KeyCode *) ckalloc(sizeof(KeyCode)*8);
+ map->modifiermap[ShiftMapIndex] = VK_SHIFT;
+ map->modifiermap[LockMapIndex] = VK_CAPITAL;
+ map->modifiermap[ControlMapIndex] = VK_CONTROL;
+ map->modifiermap[Mod1MapIndex] = VK_NUMLOCK;
+ map->modifiermap[Mod2MapIndex] = VK_MENU;
+ map->modifiermap[Mod3MapIndex] = VK_SCROLL;
+ map->modifiermap[Mod4MapIndex] = 0;
+ map->modifiermap[Mod5MapIndex] = 0;
+ return map;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XFreeModifiermap --
+ *
+ * Deallocate a modifier map that was created by
+ * XGetModifierMapping.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees the datastructure referenced by modmap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XFreeModifiermap(modmap)
+ XModifierKeymap* modmap;
+{
+ ckfree((char *) modmap->modifiermap);
+ ckfree((char *) modmap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XStringToKeysym --
+ *
+ * Translate a keysym name to the matching keysym.
+ *
+ * Results:
+ * Returns the keysym. Since this is already handled by
+ * Tk's StringToKeysym function, we just return NoSymbol.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+KeySym
+XStringToKeysym(string)
+ _Xconst char *string;
+{
+ return NoSymbol;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XKeysymToString --
+ *
+ * Convert a keysym to character form.
+ *
+ * Results:
+ * Returns NULL, since Tk will have handled this already.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+XKeysymToString(keysym)
+ KeySym keysym;
+{
+ return NULL;
+}
+
+
diff --git a/win/tkWinMenu.c b/win/tkWinMenu.c
new file mode 100644
index 0000000..00e24b7
--- /dev/null
+++ b/win/tkWinMenu.c
@@ -0,0 +1,2646 @@
+/*
+ * tkWinMenu.c --
+ *
+ * This module implements the Mac-platform specific features of menus.
+ *
+ * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinMenu.c 1.102 97/10/28 13:56:58
+ */
+
+#define OEMRESOURCE
+#include <string.h>
+#include "tkMenu.h"
+#include "tkWinInt.h"
+
+/*
+ * The class of the window for popup menus.
+ */
+
+#define MENU_CLASS_NAME "MenuWindowClass"
+
+/*
+ * Used to align a windows bitmap inside a rectangle
+ */
+
+#define ALIGN_BITMAP_LEFT 0x00000001
+#define ALIGN_BITMAP_RIGHT 0x00000002
+#define ALIGN_BITMAP_TOP 0x00000004
+#define ALIGN_BITMAP_BOTTOM 0x00000008
+
+/*
+ * Platform-specific menu flags:
+ *
+ * MENU_SYSTEM_MENU Non-zero means that the Windows menu handle
+ * was retrieved with GetSystemMenu and needs
+ * to be disposed of specially.
+ * MENU_RECONFIGURE_PENDING
+ * Non-zero means that an idle handler has
+ * been set up to reconfigure the Windows menu
+ * handle for this menu.
+ */
+
+#define MENU_SYSTEM_MENU MENU_PLATFORM_FLAG1
+#define MENU_RECONFIGURE_PENDING MENU_PLATFORM_FLAG2
+
+static int indicatorDimensions[2];
+ /* The dimensions of the indicator space
+ * in a menu entry. Calculated at init
+ * time to save time. */
+static Tcl_HashTable commandTable;
+ /* A map of command ids to menu entries */
+static int inPostMenu; /* We cannot be re-entrant like X Windows. */
+static WORD lastCommandID; /* The last command ID we allocated. */
+static HWND menuHWND; /* A window to service popup-menu messages
+ * in. */
+static int oldServiceMode; /* Used while processing a menu; we need
+ * to set the event mode specially when we
+ * enter the menu processing modal loop
+ * and reset it when menus go away. */
+static TkMenu *modalMenuPtr; /* The menu we are processing inside the modal
+ * loop. We need this to reset all of the
+ * active items when menus go away since
+ * Windows does not see fit to give this
+ * to us when it sends its WM_MENUSELECT. */
+static OSVERSIONINFO versionInfo;
+ /* So we don't have to keep doing this */
+static Tcl_HashTable winMenuTable;
+ /* Need this to map HMENUs back to menuPtrs */
+
+/*
+ * The following are default menu value strings.
+ */
+
+static char borderString[5]; /* The string indicating how big the border is */
+static Tcl_DString menuFontDString;
+ /* A buffer to store the default menu font
+ * string. */
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void DrawMenuEntryAccelerator _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Drawable d, GC gc, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr,
+ Tk_3DBorder activeBorder, int x, int y,
+ int width, int height, int drawArrow));
+static void DrawMenuEntryBackground _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Drawable d, Tk_3DBorder activeBorder,
+ Tk_3DBorder bgBorder, int x, int y,
+ int width, int heigth));
+static void DrawMenuEntryIndicator _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Drawable d, GC gc, GC indicatorGC,
+ Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int x, int y,
+ int width, int height));
+static void DrawMenuEntryLabel _ANSI_ARGS_((
+ TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d,
+ GC gc, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int x, int y,
+ int width, int height));
+static void DrawMenuSeparator _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Drawable d, GC gc,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int x, int y, int width, int height));
+static void DrawTearoffEntry _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Drawable d, GC gc,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int x, int y, int width, int height));
+static void DrawMenuUnderline _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Drawable d, GC gc,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, int x,
+ int y, int width, int height));
+static void DrawWindowsSystemBitmap _ANSI_ARGS_((
+ Display *display, Drawable drawable,
+ GC gc, CONST RECT *rectPtr, int bitmapID,
+ int alignFlags));
+static void FreeID _ANSI_ARGS_((int commandID));
+static char * GetEntryText _ANSI_ARGS_((TkMenuEntry *mePtr));
+static void GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int *widthPtr,
+ int *heightPtr));
+static void GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetMenuIndicatorGeometry _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetMenuSeparatorGeometry _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int *widthPtr,
+ int *heightPtr));
+static int GetNewID _ANSI_ARGS_((TkMenuEntry *mePtr,
+ int *menuIDPtr));
+static void MenuExitProc _ANSI_ARGS_((ClientData clientData));
+static int MenuKeyBindProc _ANSI_ARGS_((
+ ClientData clientData,
+ Tcl_Interp *interp, XEvent *eventPtr,
+ Tk_Window tkwin, KeySym keySym));
+static void MenuSelectEvent _ANSI_ARGS_((TkMenu *menuPtr));
+static void ReconfigureWindowsMenu _ANSI_ARGS_((
+ ClientData clientData));
+static void RecursivelyClearActiveMenu _ANSI_ARGS_((
+ TkMenu *menuPtr));
+static LRESULT CALLBACK TkWinMenuProc _ANSI_ARGS_((HWND hwnd,
+ UINT message, WPARAM wParam,
+ LPARAM lParam));
+
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetNewID --
+ *
+ * Allocates a new menu id and marks it in use.
+ *
+ * Results:
+ * Returns TCL_OK if succesful; TCL_ERROR if there are no more
+ * ids of the appropriate type to allocate. menuIDPtr contains
+ * the new id if succesful.
+ *
+ * Side effects:
+ * An entry is created for the menu in the command hash table,
+ * and the hash entry is stored in the appropriate field in the
+ * menu data structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetNewID(mePtr, menuIDPtr)
+ TkMenuEntry *mePtr; /* The menu we are working with */
+ int *menuIDPtr; /* The resulting id */
+{
+ int found = 0;
+ int newEntry;
+ Tcl_HashEntry *commandEntryPtr;
+ WORD returnID;
+
+ WORD curID = lastCommandID + 1;
+
+ /*
+ * The following code relies on WORD wrapping when the highest value is
+ * incremented.
+ */
+
+ while (curID != lastCommandID) {
+ commandEntryPtr = Tcl_CreateHashEntry(&commandTable,
+ (char *) curID, &newEntry);
+ if (newEntry == 1) {
+ found = 1;
+ returnID = curID;
+ break;
+ }
+ curID++;
+ }
+
+ if (found) {
+ Tcl_SetHashValue(commandEntryPtr, (char *) mePtr);
+ *menuIDPtr = (int) returnID;
+ lastCommandID = returnID;
+ return TCL_OK;
+ } else {
+ return TCL_ERROR;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeID --
+ *
+ * Marks the itemID as free.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The hash table entry for the ID is cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FreeID(commandID)
+ int commandID;
+{
+ Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&commandTable,
+ (char *) commandID);
+
+ if (entryPtr != NULL) {
+ Tcl_DeleteHashEntry(entryPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpNewMenu --
+ *
+ * Gets a new blank menu. Only the platform specific options are filled
+ * in.
+ *
+ * Results:
+ * Standard TCL error.
+ *
+ * Side effects:
+ * Allocates a Windows menu handle and places it in the platformData
+ * field of the menuPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpNewMenu(menuPtr)
+ TkMenu *menuPtr; /* The common structure we are making the
+ * platform structure for. */
+{
+ HMENU winMenuHdl;
+ Tcl_HashEntry *hashEntryPtr;
+ int newEntry;
+
+ winMenuHdl = CreatePopupMenu();
+
+ if (winMenuHdl == NULL) {
+ Tcl_AppendResult(menuPtr->interp, "No more menus can be allocated.",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * We hash all of the HMENU's so that we can get their menu ptrs
+ * back when dispatch messages.
+ */
+
+ hashEntryPtr = Tcl_CreateHashEntry(&winMenuTable, (char *) winMenuHdl,
+ &newEntry);
+ Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
+
+ menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyMenu --
+ *
+ * Destroys platform-specific menu structures.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All platform-specific allocations are freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyMenu(menuPtr)
+ TkMenu *menuPtr; /* The common menu structure */
+{
+ HMENU winMenuHdl = (HMENU) menuPtr->platformData;
+
+ if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
+ Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ }
+
+ if (NULL != winMenuHdl) {
+ if (menuPtr->menuFlags & MENU_SYSTEM_MENU) {
+ TkMenuEntry *searchEntryPtr;
+ Tcl_HashTable *tablePtr = TkGetMenuHashTable(menuPtr->interp);
+ char *menuName = Tcl_GetHashKey(tablePtr,
+ menuPtr->menuRefPtr->hashEntryPtr);
+
+ for (searchEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
+ searchEntryPtr != NULL;
+ searchEntryPtr = searchEntryPtr->nextCascadePtr) {
+ if (strcmp(searchEntryPtr->name,
+ menuName) == 0) {
+ Tk_Window parentTopLevelPtr = searchEntryPtr
+ ->menuPtr->parentTopLevelPtr;
+
+ if (parentTopLevelPtr != NULL) {
+ GetSystemMenu(TkWinGetWrapperWindow(parentTopLevelPtr),
+ TRUE);
+ }
+ break;
+ }
+ }
+ } else {
+ DestroyMenu(winMenuHdl);
+ }
+ menuPtr->platformData = NULL;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyMenuEntry --
+ *
+ * Cleans up platform-specific menu entry items.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * All platform-specific allocations are freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyMenuEntry(mePtr)
+ TkMenuEntry *mePtr; /* The entry to destroy */
+{
+ TkMenu *menuPtr = mePtr->menuPtr;
+ HMENU winMenuHdl = (HMENU) menuPtr->platformData;
+
+ if (NULL != winMenuHdl) {
+ if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
+ menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ }
+ }
+ FreeID((int) mePtr->platformEntryData);
+ mePtr->platformEntryData = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetEntryText --
+ *
+ * Given a menu entry, gives back the text that should go in it.
+ * Separators should be done by the caller, as they have to be
+ * handled specially. Allocates the memory with alloc. The caller
+ * should free the memory.
+ *
+ * Results:
+ * itemText points to the new text for the item.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+GetEntryText(mePtr)
+ TkMenuEntry *mePtr; /* A pointer to the menu entry. */
+{
+ char *itemText;
+
+ if (mePtr->type == TEAROFF_ENTRY) {
+ itemText = ckalloc(sizeof("(Tear-off)"));
+ strcpy(itemText, "(Tear-off)");
+ } else if (mePtr->imageString != NULL) {
+ itemText = ckalloc(sizeof("(Image)"));
+ strcpy(itemText, "(Image)");
+ } else if (mePtr->bitmap != None) {
+ itemText = ckalloc(sizeof("(Pixmap)"));
+ strcpy(itemText, "(Pixmap)");
+ } else if (mePtr->label == NULL || mePtr->labelLength == 0) {
+ itemText = ckalloc(sizeof("( )"));
+ strcpy(itemText, "( )");
+ } else {
+ int size = mePtr->labelLength + 1;
+ int i, j;
+
+ /*
+ * We have to construct the string with an ampersand
+ * preceeding the underline character, and a tab seperating
+ * the text and the accel text. We have to be careful with
+ * ampersands in the string.
+ */
+
+ for (i = 0; i < mePtr->labelLength; i++) {
+ if (mePtr->label[i] == '&') {
+ size++;
+ }
+ }
+
+ if (mePtr->underline >= 0) {
+ size++;
+ if (mePtr->label[mePtr->underline] == '&') {
+ size++;
+ }
+ }
+
+ if (mePtr->accelLength > 0) {
+ size += mePtr->accelLength + 1;
+ }
+
+ for (i = 0; i < mePtr->accelLength; i++) {
+ if (mePtr->accel[i] == '&') {
+ size++;
+ }
+ }
+
+ itemText = ckalloc(size);
+
+ if (mePtr->labelLength == 0) {
+ itemText[0] = 0;
+ } else {
+ for (i = 0, j = 0; i < mePtr->labelLength; i++, j++) {
+ if (mePtr->label[i] == '&') {
+ itemText[j++] = '&';
+ }
+ if (i == mePtr->underline) {
+ itemText[j++] = '&';
+ }
+ itemText[j] = mePtr->label[i];
+ }
+ itemText[j] = '\0';
+ }
+
+ if (mePtr->accelLength > 0) {
+ strcat(itemText, "\t");
+ for (i = 0, j = strlen(itemText); i < mePtr->accelLength;
+ i++, j++) {
+ if (mePtr->accel[i] == '&') {
+ itemText[j++] = '&';
+ }
+ itemText[j] = mePtr->accel[i];
+ }
+ itemText[j] = '\0';
+ }
+ }
+ return itemText;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReconfigureWindowsMenu --
+ *
+ * Tears down and rebuilds the platform-specific part of this menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Configuration information get set for mePtr; old resources
+ * get freed, if any need it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ReconfigureWindowsMenu(
+ ClientData clientData) /* The menu we are rebuilding */
+{
+ TkMenu *menuPtr = (TkMenu *) clientData;
+ TkMenuEntry *mePtr;
+ HMENU winMenuHdl = (HMENU) menuPtr->platformData;
+ char *itemText = NULL;
+ LPCTSTR lpNewItem;
+ UINT flags;
+ UINT itemID;
+ int i, count, systemMenu = 0, base;
+ int width, height;
+
+ if (NULL == winMenuHdl) {
+ return;
+ }
+
+ /*
+ * Reconstruct the entire menu. Takes care of nasty system menu and index
+ * problem.
+ *
+ */
+
+ if ((menuPtr->menuType == MENUBAR)
+ && (menuPtr->parentTopLevelPtr != NULL)) {
+ width = Tk_Width(menuPtr->parentTopLevelPtr);
+ height = Tk_Width(menuPtr->parentTopLevelPtr);
+ }
+
+ base = (menuPtr->menuFlags & MENU_SYSTEM_MENU) ? 7 : 0;
+ count = GetMenuItemCount(winMenuHdl);
+ for (i = base; i < count; i++) {
+ RemoveMenu(winMenuHdl, base, MF_BYPOSITION);
+ }
+
+ count = menuPtr->numEntries;
+ for (i = 0; i < count; i++) {
+ mePtr = menuPtr->entries[i];
+ lpNewItem = NULL;
+ flags = MF_BYPOSITION;
+ itemID = 0;
+
+ if ((menuPtr->menuType == MENUBAR) && (mePtr->type == TEAROFF_ENTRY)) {
+ continue;
+ }
+
+ if (mePtr->type == SEPARATOR_ENTRY) {
+ flags |= MF_SEPARATOR;
+ } else {
+ itemText = GetEntryText(mePtr);
+ if ((menuPtr->menuType == MENUBAR)
+ || (menuPtr->menuFlags & MENU_SYSTEM_MENU)) {
+ lpNewItem = itemText;
+ } else {
+ lpNewItem = (LPCTSTR) mePtr;
+ flags |= MF_OWNERDRAW;
+ }
+
+ /*
+ * Set enabling and disabling correctly.
+ */
+
+ if (mePtr->state == tkDisabledUid) {
+ flags |= MF_DISABLED;
+ }
+
+ /*
+ * Set the check mark for check entries and radio entries.
+ */
+
+ if (((mePtr->type == CHECK_BUTTON_ENTRY)
+ || (mePtr->type == RADIO_BUTTON_ENTRY))
+ && (mePtr->entryFlags & ENTRY_SELECTED)) {
+ flags |= MF_CHECKED;
+ }
+
+ if (mePtr->columnBreak) {
+ flags |= MF_MENUBREAK;
+ }
+
+ itemID = (int) mePtr->platformEntryData;
+ if (mePtr->type == CASCADE_ENTRY) {
+ if ((mePtr->childMenuRefPtr != NULL)
+ && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
+ HMENU childMenuHdl =
+ (HMENU) mePtr->childMenuRefPtr->menuPtr
+ ->platformData;
+ if (childMenuHdl != NULL) {
+ itemID = (UINT) childMenuHdl;
+ flags |= MF_POPUP;
+ }
+ if ((menuPtr->menuType == MENUBAR)
+ && !(mePtr->childMenuRefPtr->menuPtr->menuFlags
+ & MENU_SYSTEM_MENU)) {
+ TkMenuReferences *menuRefPtr;
+ TkMenu *systemMenuPtr = mePtr->childMenuRefPtr
+ ->menuPtr;
+ char *systemMenuName = ckalloc(strlen(
+ Tk_PathName(menuPtr->masterMenuPtr->tkwin))
+ + strlen(".system") + 1);
+
+ strcpy(systemMenuName,
+ Tk_PathName(menuPtr->masterMenuPtr->tkwin));
+ strcat(systemMenuName, ".system");
+ menuRefPtr = TkFindMenuReferences(menuPtr->interp,
+ systemMenuName);
+ if ((menuRefPtr != NULL)
+ && (menuRefPtr->menuPtr != NULL)
+ && (menuPtr->parentTopLevelPtr != NULL)
+ && (systemMenuPtr->masterMenuPtr
+ == menuRefPtr->menuPtr)) {
+ HMENU systemMenuHdl =
+ (HMENU) systemMenuPtr->platformData;
+ HWND wrapper = TkWinGetWrapperWindow(menuPtr
+ ->parentTopLevelPtr);
+ if (wrapper != NULL) {
+ DestroyMenu(systemMenuHdl);
+ systemMenuHdl = GetSystemMenu(
+ wrapper, FALSE);
+ systemMenuPtr->menuFlags |= MENU_SYSTEM_MENU;
+ systemMenuPtr->platformData =
+ (TkMenuPlatformData) systemMenuHdl;
+ if (!(systemMenuPtr->menuFlags
+ & MENU_RECONFIGURE_PENDING)) {
+ systemMenuPtr->menuFlags
+ |= MENU_RECONFIGURE_PENDING;
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu,
+ (ClientData) systemMenuPtr);
+ }
+ }
+ }
+ ckfree(systemMenuName);
+ }
+ if (mePtr->childMenuRefPtr->menuPtr->menuFlags
+ & MENU_SYSTEM_MENU) {
+ systemMenu++;
+ }
+ }
+ }
+ }
+ if (!systemMenu) {
+ InsertMenu(winMenuHdl, 0xFFFFFFFF, flags, itemID, lpNewItem);
+ }
+ if (itemText != NULL) {
+ ckfree(itemText);
+ itemText = NULL;
+ }
+ }
+
+
+ if ((menuPtr->menuType == MENUBAR)
+ && (menuPtr->parentTopLevelPtr != NULL)) {
+ DrawMenuBar(TkWinGetWrapperWindow(menuPtr->parentTopLevelPtr));
+ Tk_GeometryRequest(menuPtr->parentTopLevelPtr, width, height);
+ }
+
+ menuPtr->menuFlags &= ~(MENU_RECONFIGURE_PENDING);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpPostMenu --
+ *
+ * Posts a menu on the screen
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu is posted and handled.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpPostMenu(interp, menuPtr, x, y)
+ Tcl_Interp *interp;
+ TkMenu *menuPtr;
+ int x;
+ int y;
+{
+ HMENU winMenuHdl = (HMENU) menuPtr->platformData;
+ int result, flags;
+ RECT noGoawayRect;
+ POINT point;
+ Tk_Window parentWindow = Tk_Parent(menuPtr->tkwin);
+ int oldServiceMode = Tcl_GetServiceMode();
+
+ inPostMenu++;
+
+ if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
+ Tcl_CancelIdleCall(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ ReconfigureWindowsMenu((ClientData) menuPtr);
+ }
+
+ result = TkPreprocessMenu(menuPtr);
+ if (result != TCL_OK) {
+ inPostMenu--;
+ return result;
+ }
+
+ /*
+ * The post commands could have deleted the menu, which means
+ * we are dead and should go away.
+ */
+
+ if (menuPtr->tkwin == NULL) {
+ inPostMenu--;
+ return TCL_OK;
+ }
+
+ if (NULL == parentWindow) {
+ noGoawayRect.top = y - 50;
+ noGoawayRect.bottom = y + 50;
+ noGoawayRect.left = x - 50;
+ noGoawayRect.right = x + 50;
+ } else {
+ int left, top;
+ Tk_GetRootCoords(parentWindow, &left, &top);
+ noGoawayRect.left = left;
+ noGoawayRect.top = top;
+ noGoawayRect.bottom = noGoawayRect.top + Tk_Height(parentWindow);
+ noGoawayRect.right = noGoawayRect.left + Tk_Width(parentWindow);
+ }
+
+ Tcl_SetServiceMode(TCL_SERVICE_NONE);
+
+ /*
+ * Make an assumption here. If the right button is down,
+ * then we want to track it. Otherwise, track the left mouse button.
+ */
+
+ flags = TPM_LEFTALIGN;
+ if (GetSystemMetrics(SM_SWAPBUTTON)) {
+ if (GetAsyncKeyState(VK_LBUTTON) < 0) {
+ flags |= TPM_RIGHTBUTTON;
+ } else {
+ flags |= TPM_LEFTBUTTON;
+ }
+ } else {
+ if (GetAsyncKeyState(VK_RBUTTON) < 0) {
+ flags |= TPM_RIGHTBUTTON;
+ } else {
+ flags |= TPM_LEFTBUTTON;
+ }
+ }
+
+ TrackPopupMenu(winMenuHdl, flags, x, y, 0,
+ menuHWND, &noGoawayRect);
+ Tcl_SetServiceMode(oldServiceMode);
+
+ GetCursorPos(&point);
+ Tk_PointerEvent(NULL, point.x, point.y);
+
+ if (inPostMenu) {
+ inPostMenu = 0;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMenuNewEntry --
+ *
+ * Adds a pointer to a new menu entry structure with the platform-
+ * specific fields filled in.
+ *
+ * Results:
+ * Standard TCL error.
+ *
+ * Side effects:
+ * A new command ID is allocated and stored in the platformEntryData
+ * field of mePtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpMenuNewEntry(mePtr)
+ TkMenuEntry *mePtr;
+{
+ int commandID;
+ TkMenu *menuPtr = mePtr->menuPtr;
+
+ if (GetNewID(mePtr, &commandID) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
+ menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ }
+
+ mePtr->platformEntryData = (TkMenuPlatformEntryData) commandID;
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinMenuProc --
+ *
+ * The window proc for the dummy window we put popups in. This allows
+ * is to post a popup whether or not we know what the parent window
+ * is.
+ *
+ * Results:
+ * Returns whatever is appropriate for the message in question.
+ *
+ * Side effects:
+ * Normal side-effect for windows messages.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static LRESULT CALLBACK
+TkWinMenuProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ LRESULT lResult;
+
+ if (!TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &lResult)) {
+ lResult = DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ return lResult;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinHandleMenuEvent --
+ *
+ * Filters out menu messages from messages passed to a top-level.
+ * Will respond appropriately to WM_COMMAND, WM_MENUSELECT,
+ * WM_MEASUREITEM, WM_DRAWITEM
+ *
+ * Result:
+ * Returns 1 if this handled the message; 0 if it did not.
+ *
+ * Side effects:
+ * All of the parameters may be modified so that the caller can
+ * think it is getting a different message. plResult points to
+ * the result that should be returned to windows from this message.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkWinHandleMenuEvent(phwnd, pMessage, pwParam, plParam, plResult)
+ HWND *phwnd;
+ UINT *pMessage;
+ WPARAM *pwParam;
+ LPARAM *plParam;
+ LRESULT *plResult;
+{
+ Tcl_HashEntry *hashEntryPtr;
+ int returnResult = 0;
+ TkMenu *menuPtr;
+ TkMenuEntry *mePtr;
+
+ switch (*pMessage) {
+ case WM_INITMENU:
+ TkMenuInit();
+ hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) *pwParam);
+ if (hashEntryPtr != NULL) {
+ oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
+ modalMenuPtr = menuPtr;
+ if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
+ Tcl_CancelIdleCall(ReconfigureWindowsMenu,
+ (ClientData) menuPtr);
+ ReconfigureWindowsMenu((ClientData) menuPtr);
+ }
+ if (!inPostMenu) {
+ TkPreprocessMenu(menuPtr);
+ }
+ TkActivateMenuEntry(menuPtr, -1);
+ *plResult = 0;
+ returnResult = 1;
+ } else {
+ modalMenuPtr = NULL;
+ }
+ break;
+
+ case WM_SYSCOMMAND:
+ case WM_COMMAND: {
+ TkMenuInit();
+ if (HIWORD(*pwParam) == 0) {
+ hashEntryPtr = Tcl_FindHashEntry(&commandTable,
+ (char *)LOWORD(*pwParam));
+ if (hashEntryPtr != NULL) {
+ mePtr = (TkMenuEntry *) Tcl_GetHashValue(hashEntryPtr);
+ if (mePtr != NULL) {
+ TkMenuReferences *menuRefPtr;
+ TkMenuEntry *parentEntryPtr;
+
+ /*
+ * We have to set the parent of this menu to be active
+ * if this is a submenu so that tearoffs will get the
+ * correct title.
+ */
+
+ menuPtr = mePtr->menuPtr;
+ menuRefPtr = TkFindMenuReferences(menuPtr->interp,
+ Tk_PathName(menuPtr->tkwin));
+ if ((menuRefPtr != NULL) && (menuRefPtr->parentEntryPtr
+ != NULL)) {
+ for (parentEntryPtr = menuRefPtr->parentEntryPtr;
+ strcmp(parentEntryPtr->name,
+ Tk_PathName(menuPtr->tkwin)) != 0;
+ parentEntryPtr = parentEntryPtr->nextCascadePtr) {
+
+ /*
+ * Empty loop body.
+ */
+
+ }
+ if (parentEntryPtr->menuPtr
+ ->entries[parentEntryPtr->index]->state
+ != tkDisabledUid) {
+ TkActivateMenuEntry(parentEntryPtr->menuPtr,
+ parentEntryPtr->index);
+ }
+ }
+
+ TkInvokeMenu(mePtr->menuPtr->interp,
+ menuPtr, mePtr->index);
+ }
+ *plResult = 0;
+ returnResult = 1;
+ }
+ }
+ break;
+ }
+
+
+ case WM_MENUCHAR: {
+ unsigned char menuChar = (unsigned char) LOWORD(*pwParam);
+ hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) *plParam);
+ if (hashEntryPtr != NULL) {
+ int i;
+
+ *plResult = 0;
+ menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ int underline = menuPtr->entries[i]->underline;
+ if ((-1 != underline)
+ && (NULL != menuPtr->entries[i]->label)
+ && (CharUpper((LPTSTR) menuChar)
+ == CharUpper((LPTSTR) (unsigned char) menuPtr
+ ->entries[i]->label[underline]))) {
+ *plResult = (2 << 16) | i;
+ break;
+ }
+ }
+ returnResult = 1;
+ }
+ break;
+ }
+
+ case WM_MEASUREITEM: {
+ LPMEASUREITEMSTRUCT itemPtr = (LPMEASUREITEMSTRUCT) *plParam;
+
+ if (itemPtr != NULL) {
+ mePtr = (TkMenuEntry *) itemPtr->itemData;
+ menuPtr = mePtr->menuPtr;
+
+ TkRecomputeMenu(menuPtr);
+ itemPtr->itemHeight = mePtr->height;
+ itemPtr->itemWidth = mePtr->width;
+ if (mePtr->hideMargin) {
+ itemPtr->itemWidth += 2 - indicatorDimensions[0];
+ } else {
+ itemPtr->itemWidth += 2 * menuPtr->activeBorderWidth;
+ }
+ *plResult = 1;
+ returnResult = 1;
+ }
+ break;
+ }
+
+ case WM_DRAWITEM: {
+ TkWinDrawable *twdPtr;
+ LPDRAWITEMSTRUCT itemPtr = (LPDRAWITEMSTRUCT) *plParam;
+ Tk_FontMetrics fontMetrics;
+
+ if (itemPtr != NULL) {
+ mePtr = (TkMenuEntry *) itemPtr->itemData;
+ menuPtr = mePtr->menuPtr;
+ twdPtr = (TkWinDrawable *) ckalloc(sizeof(TkWinDrawable));
+ twdPtr->type = TWD_WINDC;
+ twdPtr->winDC.hdc = itemPtr->hDC;
+
+ if (mePtr->state != tkDisabledUid) {
+ if (itemPtr->itemState & ODS_SELECTED) {
+ TkActivateMenuEntry(menuPtr, mePtr->index);
+ } else {
+ TkActivateMenuEntry(menuPtr, -1);
+ }
+ }
+
+ Tk_GetFontMetrics(menuPtr->tkfont, &fontMetrics);
+ TkpDrawMenuEntry(mePtr, (Drawable) twdPtr, menuPtr->tkfont,
+ &fontMetrics, itemPtr->rcItem.left,
+ itemPtr->rcItem.top, itemPtr->rcItem.right
+ - itemPtr->rcItem.left, itemPtr->rcItem.bottom
+ - itemPtr->rcItem.top, 0, 0);
+
+ ckfree((char *) twdPtr);
+ *plResult = 1;
+ returnResult = 1;
+ }
+ break;
+ }
+
+ case WM_MENUSELECT: {
+ UINT flags = HIWORD(*pwParam);
+
+ TkMenuInit();
+
+ if ((flags == 0xFFFF) && (*plParam == 0)) {
+ Tcl_SetServiceMode(oldServiceMode);
+ if (modalMenuPtr != NULL) {
+ RecursivelyClearActiveMenu(modalMenuPtr);
+ }
+ } else {
+ menuPtr = NULL;
+ if (*plParam != 0) {
+ hashEntryPtr = Tcl_FindHashEntry(&winMenuTable,
+ (char *) *plParam);
+ if (hashEntryPtr != NULL) {
+ menuPtr = (TkMenu *) Tcl_GetHashValue(hashEntryPtr);
+ }
+ }
+
+ if (menuPtr != NULL) {
+ mePtr = NULL;
+ if (flags != 0xFFFF) {
+ if (flags & MF_POPUP) {
+ mePtr = menuPtr->entries[LOWORD(*pwParam)];
+ } else {
+ hashEntryPtr = Tcl_FindHashEntry(&commandTable,
+ (char *) LOWORD(*pwParam));
+ if (hashEntryPtr != NULL) {
+ mePtr = (TkMenuEntry *) Tcl_GetHashValue(hashEntryPtr);
+ }
+ }
+ }
+
+ if ((mePtr == NULL) || (mePtr->state == tkDisabledUid)) {
+ TkActivateMenuEntry(menuPtr, -1);
+ } else {
+ TkActivateMenuEntry(menuPtr, mePtr->index);
+ }
+ MenuSelectEvent(menuPtr);
+ Tcl_ServiceAll();
+ }
+ }
+ }
+ }
+ return returnResult;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RecursivelyClearActiveMenu --
+ *
+ * Recursively clears the active entry in the menu's cascade hierarchy.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Generates <<MenuSelect>> virtual events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+RecursivelyClearActiveMenu(
+ TkMenu *menuPtr) /* The menu to reset. */
+{
+ int i;
+ TkMenuEntry *mePtr;
+
+ TkActivateMenuEntry(menuPtr, -1);
+ MenuSelectEvent(menuPtr);
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ mePtr = menuPtr->entries[i];
+ if (mePtr->type == CASCADE_ENTRY) {
+ if ((mePtr->childMenuRefPtr != NULL)
+ && (mePtr->childMenuRefPtr->menuPtr != NULL)) {
+ RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr);
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetWindowMenuBar --
+ *
+ * Associates a given menu with a window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * On Windows and UNIX, associates the platform menu with the
+ * platform window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetWindowMenuBar(tkwin, menuPtr)
+ Tk_Window tkwin; /* The window we are putting the menubar into.*/
+ TkMenu *menuPtr; /* The menu we are inserting */
+{
+ HMENU winMenuHdl;
+
+ if (menuPtr != NULL) {
+ Tcl_HashEntry *hashEntryPtr;
+ int newEntry;
+
+ winMenuHdl = (HMENU) menuPtr->platformData;
+ hashEntryPtr = Tcl_FindHashEntry(&winMenuTable, (char *) winMenuHdl);
+ Tcl_DeleteHashEntry(hashEntryPtr);
+ DestroyMenu(winMenuHdl);
+ winMenuHdl = CreateMenu();
+ hashEntryPtr = Tcl_CreateHashEntry(&winMenuTable, (char *) winMenuHdl,
+ &newEntry);
+ Tcl_SetHashValue(hashEntryPtr, (char *) menuPtr);
+ menuPtr->platformData = (TkMenuPlatformData) winMenuHdl;
+ TkWinSetMenu(tkwin, winMenuHdl);
+ if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) {
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
+ }
+ } else {
+ TkWinSetMenu(tkwin, NULL);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetMainMenubar --
+ *
+ * Puts the menu associated with a window into the menubar. Should
+ * only be called when the window is in front.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menubar is changed.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+TkpSetMainMenubar(
+ Tcl_Interp *interp, /* The interpreter of the application */
+ Tk_Window tkwin, /* The frame we are setting up */
+ char *menuName) /* The name of the menu to put in front.
+ * If NULL, use the default menu bar.
+ */
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuIndicatorGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+GetMenuIndicatorGeometry (
+ TkMenu *menuPtr, /* The menu we are measuring */
+ TkMenuEntry *mePtr, /* The entry we are measuring */
+ Tk_Font tkfont, /* Precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* Precalculated font metrics */
+ int *widthPtr, /* The resulting width */
+ int *heightPtr) /* The resulting height */
+{
+ *heightPtr = indicatorDimensions[0];
+ if (mePtr->hideMargin) {
+ *widthPtr = 0;
+ } else {
+ *widthPtr = indicatorDimensions[1] - menuPtr->borderWidth;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuAccelGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+GetMenuAccelGeometry (
+ TkMenu *menuPtr, /* The menu we are measuring */
+ TkMenuEntry *mePtr, /* The entry we are measuring */
+ Tk_Font tkfont, /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* The precalculated font metrics */
+ int *widthPtr, /* The resulting width */
+ int *heightPtr) /* The resulting height */
+{
+ *heightPtr = fmPtr->linespace;
+ if (mePtr->type == CASCADE_ENTRY) {
+ *widthPtr = 0;
+ } else if (mePtr->accel == NULL) {
+ *widthPtr = 0;
+ } else {
+ *widthPtr = Tk_TextWidth(tkfont, mePtr->accel, mePtr->accelLength);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTearoffEntryGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+GetTearoffEntryGeometry (
+ TkMenu *menuPtr, /* The menu we are measuring */
+ TkMenuEntry *mePtr, /* The entry we are measuring */
+ Tk_Font tkfont, /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* The precalculated font metrics */
+ int *widthPtr, /* The resulting width */
+ int *heightPtr) /* The resulting height */
+{
+ if (menuPtr->menuType != MASTER_MENU) {
+ *heightPtr = 0;
+ } else {
+ *heightPtr = fmPtr->linespace;
+ }
+ *widthPtr = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuSeparatorGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+GetMenuSeparatorGeometry (
+ TkMenu *menuPtr, /* The menu we are measuring */
+ TkMenuEntry *mePtr, /* The entry we are measuring */
+ Tk_Font tkfont, /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* The precalcualted font metrics */
+ int *widthPtr, /* The resulting width */
+ int *heightPtr) /* The resulting height */
+{
+ *widthPtr = 0;
+ *heightPtr = fmPtr->linespace;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawWindowsSystemBitmap --
+ *
+ * Draws the windows system bitmap given by bitmapID into the rect
+ * given by rectPtr in the drawable. The bitmap is centered in the
+ * rectangle. It is not clipped, so if the bitmap is bigger than
+ * the rect it will bleed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Drawing occurs. Some storage is allocated and released.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DrawWindowsSystemBitmap(display, drawable, gc, rectPtr, bitmapID, alignFlags)
+ Display *display; /* The display we are drawing into */
+ Drawable drawable; /* The drawable we are working with */
+ GC gc; /* The GC to draw with */
+ CONST RECT *rectPtr; /* The rectangle to draw into */
+ int bitmapID; /* The windows id of the system
+ * bitmap to draw. */
+ int alignFlags; /* How to align the bitmap inside the
+ * rectangle. */
+{
+ TkWinDCState state;
+ HDC hdc = TkWinGetDrawableDC(display, drawable, &state);
+ HDC scratchDC;
+ HBITMAP bitmap;
+ BITMAP bm;
+ POINT ptSize;
+ POINT ptOrg;
+ int topOffset, leftOffset;
+
+ SetBkColor(hdc, gc->background);
+ SetTextColor(hdc, gc->foreground);
+
+ scratchDC = CreateCompatibleDC(hdc);
+ bitmap = LoadBitmap(NULL, MAKEINTRESOURCE(bitmapID));
+
+ SelectObject(scratchDC, bitmap);
+ SetMapMode(scratchDC, GetMapMode(hdc));
+ GetObject(bitmap, sizeof(BITMAP), &bm);
+ ptSize.x = bm.bmWidth;
+ ptSize.y = bm.bmHeight;
+ DPtoLP(hdc, &ptSize, 1);
+
+ ptOrg.y = ptOrg.x = 0;
+ DPtoLP(hdc, &ptOrg, 1);
+
+ if (alignFlags & ALIGN_BITMAP_TOP) {
+ topOffset = 0;
+ } else if (alignFlags & ALIGN_BITMAP_BOTTOM) {
+ topOffset = (rectPtr->bottom - rectPtr->top) - ptSize.y;
+ } else {
+ topOffset = (rectPtr->bottom - rectPtr->top) / 2 - (ptSize.y / 2);
+ }
+
+ if (alignFlags & ALIGN_BITMAP_LEFT) {
+ leftOffset = 0;
+ } else if (alignFlags & ALIGN_BITMAP_RIGHT) {
+ leftOffset = (rectPtr->right - rectPtr->left) - ptSize.x;
+ } else {
+ leftOffset = (rectPtr->right - rectPtr->left) / 2 - (ptSize.x / 2);
+ }
+
+ BitBlt(hdc, rectPtr->left + leftOffset, rectPtr->top + topOffset, ptSize.x,
+ ptSize.y, scratchDC, ptOrg.x, ptOrg.y, SRCCOPY);
+ DeleteDC(scratchDC);
+ DeleteObject(bitmap);
+
+ TkWinReleaseDrawableDC(drawable, hdc, &state);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryIndicator --
+ *
+ * This procedure draws the indicator part of a menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr, x,
+ y, width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* What we are drawing into */
+ GC gc; /* The gc we are drawing with */
+ GC indicatorGC; /* The gc for indicator objects */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated font metrics */
+ int x; /* Left edge */
+ int y; /* Top edge */
+ int width;
+ int height;
+{
+ if ((mePtr->type == CHECK_BUTTON_ENTRY ||
+ mePtr->type == RADIO_BUTTON_ENTRY)
+ && mePtr->indicatorOn
+ && mePtr->entryFlags & ENTRY_SELECTED) {
+ RECT rect;
+ GC whichGC;
+
+ if (mePtr->state != tkNormalUid) {
+ whichGC = gc;
+ } else {
+ whichGC = indicatorGC;
+ }
+
+ rect.top = y;
+ rect.bottom = y + mePtr->height;
+ rect.left = menuPtr->borderWidth + menuPtr->activeBorderWidth + x;
+ rect.right = mePtr->indicatorSpace + x;
+
+ if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg != NULL)
+ && (versionInfo.dwMajorVersion >= 4)) {
+ RECT hilightRect;
+ COLORREF oldFgColor = whichGC->foreground;
+
+ whichGC->foreground = GetSysColor(COLOR_3DHILIGHT);
+ hilightRect.top = rect.top + 1;
+ hilightRect.bottom = rect.bottom + 1;
+ hilightRect.left = rect.left + 1;
+ hilightRect.right = rect.right + 1;
+ DrawWindowsSystemBitmap(menuPtr->display, d, whichGC,
+ &hilightRect, OBM_CHECK, 0);
+ whichGC->foreground = oldFgColor;
+ }
+
+ DrawWindowsSystemBitmap(menuPtr->display, d, whichGC, &rect,
+ OBM_CHECK, 0);
+
+ if ((mePtr->state == tkDisabledUid)
+ && (menuPtr->disabledImageGC != None)
+ && (versionInfo.dwMajorVersion < 4)) {
+ XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
+ rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryAccelerator --
+ *
+ * This procedure draws the accelerator part of a menu. We
+ * need to decide what to draw here. Should we replace strings
+ * like "Control", "Command", etc?
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
+ activeBorder, x, y, width, height, drawArrow)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* What we are drawing into */
+ GC gc; /* The gc we are drawing with */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated font metrics */
+ Tk_3DBorder activeBorder; /* The border when an item is active */
+ int x; /* left edge */
+ int y; /* top edge */
+ int width; /* Width of menu entry */
+ int height; /* Height of menu entry */
+ int drawArrow; /* For cascade menus, whether of not
+ * to draw the arraw. I cannot figure
+ * out Windows' algorithm for where
+ * to draw this. */
+{
+ int baseline;
+ int leftEdge = x + mePtr->indicatorSpace + mePtr->labelWidth;
+
+ baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
+
+ if ((mePtr->state == tkDisabledUid) && (menuPtr->disabledFg != NULL)
+ && ((mePtr->accel != NULL)
+ || ((mePtr->type == CASCADE_ENTRY) && drawArrow))) {
+ if (versionInfo.dwMajorVersion >= 4) {
+ COLORREF oldFgColor = gc->foreground;
+
+ gc->foreground = GetSysColor(COLOR_3DHILIGHT);
+ if (mePtr->accel != NULL) {
+ Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->accel,
+ mePtr->accelLength, leftEdge + 1, baseline + 1);
+ }
+
+ if (mePtr->type == CASCADE_ENTRY) {
+ RECT rect;
+
+ rect.top = y + GetSystemMetrics(SM_CYBORDER) + 1;
+ rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER) + 1;
+ rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth + 1;
+ rect.right = x + width;
+ DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect,
+ OBM_MNARROW, ALIGN_BITMAP_RIGHT);
+ }
+ gc->foreground = oldFgColor;
+ }
+ }
+
+ if (mePtr->accel != NULL) {
+ Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->accel,
+ mePtr->accelLength, leftEdge, baseline);
+ }
+
+ if ((mePtr->state == tkDisabledUid)
+ && (menuPtr->disabledImageGC != None)
+ && (versionInfo.dwMajorVersion < 4)) {
+ XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
+ leftEdge, y, width - mePtr->labelWidth
+ - mePtr->indicatorSpace, height);
+ }
+
+ if ((mePtr->type == CASCADE_ENTRY) && drawArrow) {
+ RECT rect;
+
+ rect.top = y + GetSystemMetrics(SM_CYBORDER);
+ rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER);
+ rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth;
+ rect.right = x + width - 1;
+ DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect, OBM_MNARROW,
+ ALIGN_BITMAP_RIGHT);
+ if ((mePtr->state == tkDisabledUid)
+ && (menuPtr->disabledImageGC != None)
+ && (versionInfo.dwMajorVersion < 4)) {
+ XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
+ rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuSeparator --
+ *
+ * The menu separator is drawn.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+void
+DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* What we are drawing into */
+ GC gc; /* The gc we are drawing with */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated font metrics */
+ int x; /* left edge */
+ int y; /* top edge */
+ int width; /* width of item */
+ int height; /* height of item */
+{
+ XPoint points[2];
+
+ points[0].x = x;
+ points[0].y = y + height / 2;
+ points[1].x = x + width - 1;
+ points[1].y = points[0].y;
+ Tk_Draw3DPolygon(menuPtr->tkwin, d,
+ menuPtr->border, points, 2, 1, TK_RELIEF_RAISED);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuUnderline --
+ *
+ * On appropriate platforms, draw the underline character for the
+ * menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+DrawMenuUnderline(
+ TkMenu *menuPtr, /* The menu to draw into */
+ TkMenuEntry *mePtr, /* The entry we are drawing */
+ Drawable d, /* What we are drawing into */
+ GC gc, /* The gc to draw into */
+ Tk_Font tkfont, /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* The precalculated font metrics */
+ int x, /* Left Edge */
+ int y, /* Top Edge */
+ int width, /* Width of entry */
+ int height) /* Height of entry */
+{
+ if (mePtr->underline >= 0) {
+ Tk_UnderlineChars(menuPtr->display, d,
+ gc, tkfont, mePtr->label, x + mePtr->indicatorSpace,
+ y + (height + fmPtr->ascent - fmPtr->descent) / 2,
+ mePtr->underline, mePtr->underline + 1);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * MenuKeyBindProc --
+ *
+ * This procedure is invoked when keys related to pulling
+ * down menus is pressed. The corresponding Windows events
+ * are generated and passed to DefWindowProc if appropriate.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ * Side effects:
+ * The menu system may take over and process user events
+ * for menu input.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+MenuKeyBindProc(clientData, interp, eventPtr, tkwin, keySym)
+ ClientData clientData; /* not used in this proc */
+ Tcl_Interp *interp; /* The interpreter of the receiving window. */
+ XEvent *eventPtr; /* The XEvent to process */
+ Tk_Window tkwin; /* The window receiving the event */
+ KeySym keySym; /* The key sym that is produced. */
+{
+ UINT scanCode;
+ UINT virtualKey;
+ TkWindow *winPtr = (TkWindow *)tkwin;
+
+ if (eventPtr->type == KeyPress) {
+ switch (keySym) {
+ case XK_Alt_L:
+ scanCode = MapVirtualKey(VK_LMENU, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
+ | (1 << 29));
+ break;
+ case XK_Alt_R:
+ scanCode = MapVirtualKey(VK_RMENU, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYDOWN, VK_MENU, (scanCode << 16)
+ | (1 << 29) | (1 << 24));
+ break;
+ case XK_F10:
+ scanCode = MapVirtualKey(VK_F10, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYDOWN, VK_F10, (scanCode << 16));
+ break;
+ default:
+ virtualKey = XKeysymToKeycode(winPtr->display, keySym);
+ scanCode = MapVirtualKey(virtualKey, 0);
+ if (0 != scanCode) {
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYDOWN, virtualKey, ((scanCode << 16)
+ | (1 << 29)));
+ }
+ }
+ } else if (eventPtr->type == KeyRelease) {
+ switch (keySym) {
+ case XK_Alt_L:
+ scanCode = MapVirtualKey(VK_LMENU, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYUP, VK_MENU, (scanCode << 16)
+ | (1 << 29) | (1 << 30) | (1 << 31));
+ break;
+ case XK_Alt_R:
+ scanCode = MapVirtualKey(VK_RMENU, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYUP, VK_MENU, (scanCode << 16) | (1 << 24)
+ | (0x111 << 29) | (1 << 30) | (1 << 31));
+ break;
+ case XK_F10:
+ scanCode = MapVirtualKey(VK_F10, 0);
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYUP, VK_F10, (scanCode << 16)
+ | (1 << 30) | (1 << 31));
+ break;
+ default:
+ virtualKey = XKeysymToKeycode(winPtr->display, keySym);
+ scanCode = MapVirtualKey(virtualKey, 0);
+ if (0 != scanCode) {
+ CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)),
+ WM_SYSKEYUP, virtualKey, ((scanCode << 16)
+ | (1 << 29) | (1 << 30) | (1 << 31)));
+ }
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpInitializeMenuBindings --
+ *
+ * For every interp, initializes the bindings for Windows
+ * menus. Does nothing on Mac or XWindows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * C-level bindings are setup for the interp which will
+ * handle Alt-key sequences for menus without beeping
+ * or interfering with user-defined Alt-key bindings.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpInitializeMenuBindings(interp, bindingTable)
+ Tcl_Interp *interp; /* The interpreter to set. */
+ Tk_BindingTable bindingTable; /* The table to add to. */
+{
+ Tk_Uid uid = Tk_GetUid("all");
+
+ /*
+ * We need to set up the bindings for menubars. These have to
+ * recreate windows events, so we need to have a C-level
+ * binding for this. We have to generate the WM_SYSKEYDOWNS
+ * and WM_SYSKEYUPs appropriately.
+ */
+
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<Alt_L>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<KeyRelease-Alt_L>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<Alt_R>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<KeyRelease-Alt_R>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<Alt-KeyPress>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<Alt-KeyRelease>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<KeyPress-F10>", MenuKeyBindProc, NULL, NULL);
+ TkCreateBindingProcedure(interp, bindingTable, (ClientData)uid,
+ "<KeyRelease-F10>", MenuKeyBindProc, NULL, NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryLabel --
+ *
+ * This procedure draws the label part of a menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DrawMenuEntryLabel(
+ TkMenu *menuPtr, /* The menu we are drawing */
+ TkMenuEntry *mePtr, /* The entry we are drawing */
+ Drawable d, /* What we are drawing into */
+ GC gc, /* The gc we are drawing into */
+ Tk_Font tkfont, /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr, /* The precalculated font metrics */
+ int x, /* left edge */
+ int y, /* right edge */
+ int width, /* width of entry */
+ int height) /* height of entry */
+{
+ int baseline;
+ int indicatorSpace = mePtr->indicatorSpace;
+ int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth;
+ int imageHeight, imageWidth;
+
+ /*
+ * Draw label or bitmap or image for entry.
+ */
+
+ baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
+ if (mePtr->image != NULL) {
+ Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
+ if ((mePtr->selectImage != NULL)
+ && (mePtr->entryFlags & ENTRY_SELECTED)) {
+ Tk_RedrawImage(mePtr->selectImage, 0, 0,
+ imageWidth, imageHeight, d, leftEdge,
+ (int) (y + (mePtr->height - imageHeight)/2));
+ } else {
+ Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
+ imageHeight, d, leftEdge,
+ (int) (y + (mePtr->height - imageHeight)/2));
+ }
+ } else if (mePtr->bitmap != None) {
+ int width, height;
+
+ Tk_SizeOfBitmap(menuPtr->display,
+ mePtr->bitmap, &width, &height);
+ XCopyPlane(menuPtr->display,
+ mePtr->bitmap, d,
+ gc, 0, 0, (unsigned) width, (unsigned) height, leftEdge,
+ (int) (y + (mePtr->height - height)/2), 1);
+ } else {
+ if (mePtr->labelLength > 0) {
+ Tk_DrawChars(menuPtr->display, d, gc,
+ tkfont, mePtr->label, mePtr->labelLength,
+ leftEdge, baseline);
+ DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y,
+ width, height);
+ }
+ }
+
+ if (mePtr->state == tkDisabledUid) {
+ if (menuPtr->disabledFg == NULL) {
+ XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
+ (unsigned) width, (unsigned) height);
+ } else if ((mePtr->image != NULL)
+ && (menuPtr->disabledImageGC != None)) {
+ XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
+ leftEdge,
+ (int) (y + (mePtr->height - imageHeight)/2),
+ (unsigned) imageWidth, (unsigned) imageHeight);
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpComputeMenubarGeometry --
+ *
+ * This procedure is invoked to recompute the size and
+ * layout of a menu that is a menubar clone.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Fields of menu entries are changed to reflect their
+ * current positions, and the size of the menu window
+ * itself may be changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpComputeMenubarGeometry(menuPtr)
+ TkMenu *menuPtr; /* Structure describing menu. */
+{
+ TkpComputeStandardMenuGeometry(menuPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawTearoffEntry --
+ *
+ * This procedure draws the background part of a menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* The drawable we are drawing into */
+ GC gc; /* The gc we are drawing with */
+ Tk_Font tkfont; /* The font we are drawing with */
+ CONST Tk_FontMetrics *fmPtr; /* The metrics we are drawing with */
+ int x;
+ int y;
+ int width;
+ int height;
+{
+ XPoint points[2];
+ int segmentWidth, maxX;
+
+ if (menuPtr->menuType != MASTER_MENU) {
+ return;
+ }
+
+ points[0].x = x;
+ points[0].y = y + height/2;
+ points[1].y = points[0].y;
+ segmentWidth = 6;
+ maxX = width - 1;
+
+ while (points[0].x < maxX) {
+ points[1].x = points[0].x + segmentWidth;
+ if (points[1].x > maxX) {
+ points[1].x = maxX;
+ }
+ Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 2, 1,
+ TK_RELIEF_RAISED);
+ points[0].x += 2*segmentWidth;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpConfigureMenuEntry --
+ *
+ * Processes configurations for menu entries.
+ *
+ * Results:
+ * Returns standard TCL result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information get set for mePtr; old resources
+ * get freed, if any need it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpConfigureMenuEntry(mePtr)
+ register TkMenuEntry *mePtr; /* Information about menu entry; may
+ * or may not already have values for
+ * some fields. */
+{
+ TkMenu *menuPtr = mePtr->menuPtr;
+
+ if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
+ menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu, (ClientData) menuPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDrawMenuEntry --
+ *
+ * Draws the given menu entry at the given coordinates with the
+ * given attributes.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * X Server commands are executed to display the menu entry.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
+ strictMotif, drawArrow)
+ TkMenuEntry *mePtr; /* The entry to draw */
+ Drawable d; /* What to draw into */
+ Tk_Font tkfont; /* Precalculated font for menu */
+ CONST Tk_FontMetrics *menuMetricsPtr;
+ /* Precalculated metrics for menu */
+ int x; /* X-coordinate of topleft of entry */
+ int y; /* Y-coordinate of topleft of entry */
+ int width; /* Width of the entry rectangle */
+ int height; /* Height of the current rectangle */
+ int strictMotif; /* Boolean flag */
+ int drawArrow; /* Whether or not to draw the cascade
+ * arrow for cascade items. Only applies
+ * to Windows. */
+{
+ GC gc, indicatorGC;
+ TkMenu *menuPtr = mePtr->menuPtr;
+ Tk_3DBorder bgBorder, activeBorder;
+ CONST Tk_FontMetrics *fmPtr;
+ Tk_FontMetrics entryMetrics;
+ int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
+ int adjustedY = y + padY;
+ int adjustedHeight = height - 2 * padY;
+
+ /*
+ * Choose the gc for drawing the foreground part of the entry.
+ */
+
+ if ((mePtr->state == tkActiveUid)
+ && !strictMotif) {
+ gc = mePtr->activeGC;
+ if (gc == NULL) {
+ gc = menuPtr->activeGC;
+ }
+ } else {
+ TkMenuEntry *cascadeEntryPtr;
+ int parentDisabled = 0;
+
+ for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
+ cascadeEntryPtr != NULL;
+ cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
+ if (strcmp(cascadeEntryPtr->name,
+ Tk_PathName(menuPtr->tkwin)) == 0) {
+ if (cascadeEntryPtr->state == tkDisabledUid) {
+ parentDisabled = 1;
+ }
+ break;
+ }
+ }
+
+ if (((parentDisabled || (mePtr->state == tkDisabledUid)))
+ && (menuPtr->disabledFg != NULL)) {
+ gc = mePtr->disabledGC;
+ if (gc == NULL) {
+ gc = menuPtr->disabledGC;
+ }
+ } else {
+ gc = mePtr->textGC;
+ if (gc == NULL) {
+ gc = menuPtr->textGC;
+ }
+ }
+ }
+ indicatorGC = mePtr->indicatorGC;
+ if (indicatorGC == NULL) {
+ indicatorGC = menuPtr->indicatorGC;
+ }
+
+ bgBorder = mePtr->border;
+ if (bgBorder == NULL) {
+ bgBorder = menuPtr->border;
+ }
+ if (strictMotif) {
+ activeBorder = bgBorder;
+ } else {
+ activeBorder = mePtr->activeBorder;
+ if (activeBorder == NULL) {
+ activeBorder = menuPtr->activeBorder;
+ }
+ }
+
+ if (mePtr->tkfont == NULL) {
+ fmPtr = menuMetricsPtr;
+ } else {
+ tkfont = mePtr->tkfont;
+ Tk_GetFontMetrics(tkfont, &entryMetrics);
+ fmPtr = &entryMetrics;
+ }
+
+ /*
+ * Need to draw the entire background, including padding. On Unix,
+ * for menubars, we have to draw the rest of the entry taking
+ * into account the padding.
+ */
+
+ DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
+ bgBorder, x, y, width, height);
+
+ if (mePtr->type == SEPARATOR_ENTRY) {
+ DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
+ fmPtr, x, adjustedY, width, adjustedHeight);
+ } else if (mePtr->type == TEAROFF_ENTRY) {
+ DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
+ width, adjustedHeight);
+ } else {
+ DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
+ width, adjustedHeight);
+ DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
+ activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
+ if (!mePtr->hideMargin) {
+ DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
+ fmPtr, x, adjustedY, width, adjustedHeight);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuLabelGeometry --
+ *
+ * Figures out the size of the label portion of a menu item.
+ *
+ * Results:
+ * widthPtr and heightPtr are filled in with the correct geometry
+ * information.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
+ TkMenuEntry *mePtr; /* The entry we are computing */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
+ int *widthPtr; /* The resulting width of the label
+ * portion */
+ int *heightPtr; /* The resulting height of the label
+ * portion */
+{
+ TkMenu *menuPtr = mePtr->menuPtr;
+
+ if (mePtr->image != NULL) {
+ Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
+ } else if (mePtr->bitmap != (Pixmap) NULL) {
+ Tk_SizeOfBitmap(menuPtr->display, mePtr->bitmap, widthPtr, heightPtr);
+ } else {
+ *heightPtr = fmPtr->linespace;
+
+ if (mePtr->label != NULL) {
+ *widthPtr = Tk_TextWidth(tkfont, mePtr->label, mePtr->labelLength);
+ } else {
+ *widthPtr = 0;
+ }
+ }
+ *heightPtr += 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryBackground --
+ *
+ * This procedure draws the background part of a menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DrawMenuEntryBackground(
+ TkMenu *menuPtr, /* The menu we are drawing. */
+ TkMenuEntry *mePtr, /* The entry we are drawing. */
+ Drawable d, /* What we are drawing into */
+ Tk_3DBorder activeBorder, /* Border for active items */
+ Tk_3DBorder bgBorder, /* Border for the background */
+ int x, /* left edge */
+ int y, /* top edge */
+ int width, /* width of rectangle to draw */
+ int height) /* height of rectangle to draw */
+{
+ if (mePtr->state == tkActiveUid) {
+ bgBorder = activeBorder;
+ }
+ Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder,
+ x, y, width, height, 0, TK_RELIEF_FLAT);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpComputeStandardMenuGeometry --
+ *
+ * This procedure is invoked to recompute the size and
+ * layout of a menu that is not a menubar clone.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Fields of menu entries are changed to reflect their
+ * current positions, and the size of the menu window
+ * itself may be changed.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpComputeStandardMenuGeometry(
+ TkMenu *menuPtr) /* Structure describing menu. */
+{
+ Tk_Font tkfont;
+ Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
+ int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
+ int windowWidth, windowHeight, accelSpace;
+ int i, j, lastColumnBreak = 0;
+
+ if (menuPtr->tkwin == NULL) {
+ return;
+ }
+
+ x = y = menuPtr->borderWidth;
+ indicatorSpace = labelWidth = accelWidth = 0;
+ windowHeight = 0;
+
+ /*
+ * On the Mac especially, getting font metrics can be quite slow,
+ * so we want to do it intelligently. We are going to precalculate
+ * them and pass them down to all of the measuring and drawing
+ * routines. We will measure the font metrics of the menu once.
+ * If an entry does not have its own font set, then we give
+ * the geometry/drawing routines the menu's font and metrics.
+ * If an entry has its own font, we will measure that font and
+ * give all of the geometry/drawing the entry's font and metrics.
+ */
+
+ Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics);
+ accelSpace = Tk_TextWidth(menuPtr->tkfont, "M", 1);
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ tkfont = menuPtr->entries[i]->tkfont;
+ if (tkfont == NULL) {
+ tkfont = menuPtr->tkfont;
+ fmPtr = &menuMetrics;
+ } else {
+ Tk_GetFontMetrics(tkfont, &entryMetrics);
+ fmPtr = &entryMetrics;
+ }
+
+ if ((i > 0) && menuPtr->entries[i]->columnBreak) {
+ if (accelWidth != 0) {
+ labelWidth += accelSpace;
+ }
+ for (j = lastColumnBreak; j < i; j++) {
+ menuPtr->entries[j]->indicatorSpace = indicatorSpace;
+ menuPtr->entries[j]->labelWidth = labelWidth;
+ menuPtr->entries[j]->width = indicatorSpace + labelWidth
+ + accelWidth + 2 * menuPtr->activeBorderWidth;
+ menuPtr->entries[j]->x = x;
+ menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
+ }
+ x += indicatorSpace + labelWidth + accelWidth
+ + 2 * menuPtr->borderWidth;
+ indicatorSpace = labelWidth = accelWidth = 0;
+ lastColumnBreak = i;
+ y = menuPtr->borderWidth;
+ }
+
+ if (menuPtr->entries[i]->type == SEPARATOR_ENTRY) {
+ GetMenuSeparatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
+ fmPtr, &width, &height);
+ menuPtr->entries[i]->height = height;
+ } else if (menuPtr->entries[i]->type == TEAROFF_ENTRY) {
+ GetTearoffEntryGeometry(menuPtr, menuPtr->entries[i], tkfont,
+ fmPtr, &width, &height);
+ menuPtr->entries[i]->height = height;
+ } else {
+
+ /*
+ * For each entry, compute the height required by that
+ * particular entry, plus three widths: the width of the
+ * label, the width to allow for an indicator to be displayed
+ * to the left of the label (if any), and the width of the
+ * accelerator to be displayed to the right of the label
+ * (if any). These sizes depend, of course, on the type
+ * of the entry.
+ */
+
+ GetMenuLabelGeometry(menuPtr->entries[i], tkfont, fmPtr, &width,
+ &height);
+ menuPtr->entries[i]->height = height;
+ if (width > labelWidth) {
+ labelWidth = width;
+ }
+
+ GetMenuAccelGeometry(menuPtr, menuPtr->entries[i], tkfont,
+ fmPtr, &width, &height);
+ if (height > menuPtr->entries[i]->height) {
+ menuPtr->entries[i]->height = height;
+ }
+ if (width > accelWidth) {
+ accelWidth = width;
+ }
+
+ GetMenuIndicatorGeometry(menuPtr, menuPtr->entries[i], tkfont,
+ fmPtr, &width, &height);
+ if (height > menuPtr->entries[i]->height) {
+ menuPtr->entries[i]->height = height;
+ }
+ if (width > indicatorSpace) {
+ indicatorSpace = width;
+ }
+
+ menuPtr->entries[i]->height += 2 * menuPtr->activeBorderWidth + 1;
+ }
+ menuPtr->entries[i]->y = y;
+ y += menuPtr->entries[i]->height;
+ if (y > windowHeight) {
+ windowHeight = y;
+ }
+ }
+
+ if (accelWidth != 0) {
+ labelWidth += accelSpace;
+ }
+ for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
+ menuPtr->entries[j]->indicatorSpace = indicatorSpace;
+ menuPtr->entries[j]->labelWidth = labelWidth;
+ menuPtr->entries[j]->width = indicatorSpace + labelWidth
+ + accelWidth + 2 * menuPtr->activeBorderWidth;
+ menuPtr->entries[j]->x = x;
+ menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
+ }
+ windowWidth = x + indicatorSpace + labelWidth + accelWidth + accelSpace
+ + 2 * menuPtr->activeBorderWidth
+ + 2 * menuPtr->borderWidth;
+
+
+ windowHeight += menuPtr->borderWidth;
+
+ /*
+ * The X server doesn't like zero dimensions, so round up to at least
+ * 1 (a zero-sized menu should never really occur, anyway).
+ */
+
+ if (windowWidth <= 0) {
+ windowWidth = 1;
+ }
+ if (windowHeight <= 0) {
+ windowHeight = 1;
+ }
+ menuPtr->totalWidth = windowWidth;
+ menuPtr->totalHeight = windowHeight;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuSelectEvent --
+ *
+ * Generates a "MenuSelect" virtual event. This can be used to
+ * do context-sensitive menu help.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Places a virtual event on the event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuSelectEvent(
+ TkMenu *menuPtr) /* the menu we have selected. */
+{
+ XVirtualEvent event;
+ POINTS rootPoint;
+ DWORD msgPos;
+
+ event.type = VirtualEvent;
+ event.serial = menuPtr->display->request;
+ event.send_event = 0;
+ event.display = menuPtr->display;
+ Tk_MakeWindowExist(menuPtr->tkwin);
+ event.event = Tk_WindowId(menuPtr->tkwin);
+ event.root = XRootWindow(menuPtr->display, 0);
+ event.subwindow = None;
+ event.time = TkpGetMS();
+
+ msgPos = GetMessagePos();
+ rootPoint = MAKEPOINTS(msgPos);
+ event.x_root = rootPoint.x;
+ event.y_root = rootPoint.y;
+ event.state = TkWinGetModifierState();
+ event.same_screen = 1;
+ event.name = Tk_GetUid("MenuSelect");
+ Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMenuNotifyToplevelCreate --
+ *
+ * This routine reconfigures the menu and the clones indicated by
+ * menuName becuase a toplevel has been created and any system
+ * menus need to be created.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * An idle handler is set up to do the reconfiguration.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMenuNotifyToplevelCreate(
+ Tcl_Interp *interp, /* The interp the menu lives in. */
+ char *menuName) /* The name of the menu to
+ * reconfigure. */
+{
+ TkMenuReferences *menuRefPtr;
+ TkMenu *menuPtr;
+
+ if ((menuName != NULL) && (menuName[0] != '\0')) {
+ menuRefPtr = TkFindMenuReferences(interp, menuName);
+ if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
+ for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; menuPtr != NULL;
+ menuPtr = menuPtr->nextInstancePtr) {
+ if ((menuPtr->menuType == MENUBAR)
+ && !(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) {
+ menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING;
+ Tcl_DoWhenIdle(ReconfigureWindowsMenu,
+ (ClientData) menuPtr);
+ }
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenuExitHandler --
+ *
+ * Throws away the utility window needed for menus and unregisters
+ * the class.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Menus have to be reinitialized next time.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenuExitHandler(
+ ClientData clientData) /* Not used */
+{
+ DestroyWindow(menuHWND);
+ UnregisterClass(MENU_CLASS_NAME, Tk_GetHINSTANCE());
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMenuInit --
+ *
+ * Sets up the hash tables and the variables used by the menu package.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * lastMenuID gets initialized, and the parent hash and the command hash
+ * are allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMenuInit()
+{
+ WNDCLASS wndClass;
+ char sizeString[4];
+ char faceName[LF_FACESIZE];
+ HDC scratchDC;
+ Tcl_DString boldItalicDString;
+ int bold = 0;
+ int italic = 0;
+ int i;
+ TEXTMETRIC tm;
+
+ Tcl_InitHashTable(&winMenuTable, TCL_ONE_WORD_KEYS);
+ Tcl_InitHashTable(&commandTable, TCL_ONE_WORD_KEYS);
+
+ wndClass.style = CS_OWNDC;
+ wndClass.lpfnWndProc = TkWinMenuProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = Tk_GetHINSTANCE();
+ wndClass.hIcon = NULL;
+ wndClass.hCursor = NULL;
+ wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = MENU_CLASS_NAME;
+ RegisterClass(&wndClass);
+
+ menuHWND = CreateWindow(MENU_CLASS_NAME, "MenuWindow", WS_POPUP,
+ 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL);
+
+ Tcl_CreateExitHandler(MenuExitHandler, (ClientData) NULL);
+
+ versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+
+ /*
+ * If GetVersionEx fails, it means that the version info record
+ * is too big for what is compiled. Should never happen, but if
+ * it does, we are later than Windows 95 or NT 4.0.
+ */
+
+ if (!GetVersionEx(&versionInfo)) {
+ versionInfo.dwMajorVersion = 4;
+ }
+
+ /*
+ * Set all of the default options. The loop will terminate when we run
+ * out of options via a break statement.
+ */
+
+ for (i = 0; ; i++) {
+ if (tkMenuConfigSpecs[i].type == TK_CONFIG_END) {
+ break;
+ }
+
+ if ((strcmp(tkMenuConfigSpecs[i].dbName,
+ "activeBorderWidth") == 0) ||
+ (strcmp(tkMenuConfigSpecs[i].dbName, "borderWidth") == 0)) {
+ int borderWidth;
+
+ borderWidth = GetSystemMetrics(SM_CXBORDER);
+ if (GetSystemMetrics(SM_CYBORDER) > borderWidth) {
+ borderWidth = GetSystemMetrics(SM_CYBORDER);
+ }
+ sprintf(borderString, "%d", borderWidth);
+ tkMenuConfigSpecs[i].defValue = borderString;
+ } else if ((strcmp(tkMenuConfigSpecs[i].dbName, "font") == 0)) {
+ int pointSize;
+ HFONT menuFont;
+
+ scratchDC = CreateDC("DISPLAY", NULL, NULL, NULL);
+ Tcl_DStringInit(&menuFontDString);
+
+ if (versionInfo.dwMajorVersion >= 4) {
+ NONCLIENTMETRICS ncMetrics;
+
+ ncMetrics.cbSize = sizeof(ncMetrics);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncMetrics),
+ &ncMetrics, 0);
+ menuFont = CreateFontIndirect(&ncMetrics.lfMenuFont);
+ } else {
+ menuFont = GetStockObject(SYSTEM_FONT);
+ }
+ SelectObject(scratchDC, menuFont);
+ GetTextMetrics(scratchDC, &tm);
+ GetTextFace(scratchDC, sizeof(menuFontDString), faceName);
+ pointSize = MulDiv(tm.tmHeight - tm.tmInternalLeading,
+ 72, GetDeviceCaps(scratchDC, LOGPIXELSY));
+ if (tm.tmWeight >= 700) {
+ bold = 1;
+ }
+ if (tm.tmItalic) {
+ italic = 1;
+ }
+
+ SelectObject(scratchDC, GetStockObject(SYSTEM_FONT));
+ DeleteDC(scratchDC);
+
+ DeleteObject(menuFont);
+
+ Tcl_DStringAppendElement(&menuFontDString, faceName);
+ sprintf(sizeString, "%d", pointSize);
+ Tcl_DStringAppendElement(&menuFontDString, sizeString);
+
+ if (bold == 1 || italic == 1) {
+ Tcl_DStringInit(&boldItalicDString);
+ if (bold == 1) {
+ Tcl_DStringAppendElement(&boldItalicDString, "bold");
+ }
+ if (italic == 1) {
+ Tcl_DStringAppendElement(&boldItalicDString, "italic");
+ }
+ Tcl_DStringAppendElement(&menuFontDString,
+ Tcl_DStringValue(&boldItalicDString));
+ }
+
+ tkMenuConfigSpecs[i].defValue = Tcl_DStringValue(&menuFontDString);
+ }
+ }
+
+ /*
+ * Now we go ahead and get the dimensions of the check mark and the
+ * appropriate margins. Since this is fairly hairy, we do it here
+ * to save time when traversing large sets of menu items.
+ *
+ * The code below was given to me by Microsoft over the phone. It
+ * is the only way to insure menu items lining up, and is not
+ * documented.
+ */
+
+ if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+ indicatorDimensions[0] = GetSystemMetrics(SM_CYMENUCHECK);
+ indicatorDimensions[1] = ((GetSystemMetrics(SM_CXFIXEDFRAME) +
+ GetSystemMetrics(SM_CXBORDER)
+ + GetSystemMetrics(SM_CXMENUCHECK) + 7) & 0xFFF8)
+ - GetSystemMetrics(SM_CXFIXEDFRAME);
+ } else {
+ DWORD dimensions = GetMenuCheckMarkDimensions();
+ indicatorDimensions[0] = HIWORD(dimensions);
+ indicatorDimensions[1] = LOWORD(dimensions);
+ }
+
+}
diff --git a/win/tkWinPixmap.c b/win/tkWinPixmap.c
new file mode 100644
index 0000000..1ca2d7e
--- /dev/null
+++ b/win/tkWinPixmap.c
@@ -0,0 +1,184 @@
+/*
+ * tkWinPixmap.c --
+ *
+ * This file contains the Xlib emulation functions pertaining to
+ * creating and destroying pixmaps.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinPixmap.c 1.18 97/08/06 15:36:23
+ */
+
+#include "tkWinInt.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetPixmap --
+ *
+ * Creates an in memory drawing surface.
+ *
+ * Results:
+ * Returns a handle to a new pixmap.
+ *
+ * Side effects:
+ * Allocates a new Win32 bitmap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Pixmap
+Tk_GetPixmap(display, d, width, height, depth)
+ Display* display;
+ Drawable d;
+ int width;
+ int height;
+ int depth;
+{
+ TkWinDrawable *newTwdPtr, *twdPtr;
+ int planes;
+ Screen *screen;
+
+ display->request++;
+
+ newTwdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
+ newTwdPtr->type = TWD_BITMAP;
+ newTwdPtr->bitmap.depth = depth;
+ twdPtr = (TkWinDrawable *)d;
+ if (twdPtr->type != TWD_BITMAP) {
+ if (twdPtr->window.winPtr == NULL) {
+ newTwdPtr->bitmap.colormap = DefaultColormap(display,
+ DefaultScreen(display));
+ } else {
+ newTwdPtr->bitmap.colormap = twdPtr->window.winPtr->atts.colormap;
+ }
+ } else {
+ newTwdPtr->bitmap.colormap = twdPtr->bitmap.colormap;
+ }
+ screen = &display->screens[0];
+ planes = 1;
+ if (depth == screen->root_depth) {
+ planes = (int) screen->ext_data;
+ depth /= planes;
+ }
+ newTwdPtr->bitmap.handle = CreateBitmap(width, height, planes, depth, NULL);
+
+ if (newTwdPtr->bitmap.handle == NULL) {
+ ckfree((char *) newTwdPtr);
+ return None;
+ }
+
+ return (Pixmap)newTwdPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_FreePixmap --
+ *
+ * Release the resources associated with a pixmap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Deletes the bitmap created by Tk_GetPixmap.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_FreePixmap(display, pixmap)
+ Display* display;
+ Pixmap pixmap;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *) pixmap;
+
+ display->request++;
+ if (twdPtr != NULL) {
+ DeleteObject(twdPtr->bitmap.handle);
+ ckfree((char *)twdPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSetPixmapColormap --
+ *
+ * The following function is a hack used by the photo widget to
+ * explicitly set the colormap slot of a Pixmap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkSetPixmapColormap(pixmap, colormap)
+ Pixmap pixmap;
+ Colormap colormap;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *)pixmap;
+ twdPtr->bitmap.colormap = colormap;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGetGeometry --
+ *
+ * Retrieve the geometry of the given drawable. Note that
+ * this is a degenerate implementation that only returns the
+ * size of a pixmap.
+ *
+ * Results:
+ * Returns 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XGetGeometry(display, d, root_return, x_return, y_return, width_return,
+ height_return, border_width_return, depth_return)
+ Display* display;
+ Drawable d;
+ Window* root_return;
+ int* x_return;
+ int* y_return;
+ unsigned int* width_return;
+ unsigned int* height_return;
+ unsigned int* border_width_return;
+ unsigned int* depth_return;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *)d;
+ HDC dc;
+ BITMAPINFO info;
+
+ if ((twdPtr->type != TWD_BITMAP) || (twdPtr->bitmap.handle == NULL)) {
+ panic("XGetGeometry: invalid pixmap");
+ }
+ dc = GetDC(NULL);
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biBitCount = 0;
+ if (!GetDIBits(dc, twdPtr->bitmap.handle, 0, 0, NULL, &info,
+ DIB_RGB_COLORS)) {
+ panic("XGetGeometry: unable to get bitmap size");
+ }
+ ReleaseDC(NULL, dc);
+
+ *width_return = info.bmiHeader.biWidth;
+ *height_return = info.bmiHeader.biHeight;
+ return 1;
+}
diff --git a/win/tkWinPointer.c b/win/tkWinPointer.c
new file mode 100644
index 0000000..96661ae
--- /dev/null
+++ b/win/tkWinPointer.c
@@ -0,0 +1,457 @@
+/*
+ * tkWinPointer.c --
+ *
+ * Windows specific mouse tracking code.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinPointer.c 1.28 97/10/31 08:40:07
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * Check for enter/leave events every MOUSE_TIMER_INTERVAL milliseconds.
+ */
+
+#define MOUSE_TIMER_INTERVAL 250
+
+/*
+ * Declarations of static variables used in this file.
+ */
+
+static int captured = 0; /* 1 if mouse is currently captured. */
+static TkWindow *keyboardWinPtr = NULL; /* Current keyboard grab window. */
+static Tcl_TimerToken mouseTimer; /* Handle to the latest mouse timer. */
+static int mouseTimerSet = 0; /* 1 if the mouse timer is active. */
+
+/*
+ * Forward declarations of procedures used in this file.
+ */
+
+static void MouseTimerProc _ANSI_ARGS_((ClientData clientData));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetModifierState --
+ *
+ * Return the modifier state as of the last message.
+ *
+ * Results:
+ * Returns the X modifier mask.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkWinGetModifierState()
+{
+ int state = 0;
+
+ if (GetKeyState(VK_SHIFT) & 0x8000) {
+ state |= ShiftMask;
+ }
+ if (GetKeyState(VK_CONTROL) & 0x8000) {
+ state |= ControlMask;
+ }
+ if (GetKeyState(VK_MENU) & 0x8000) {
+ state |= Mod2Mask;
+ }
+ if (GetKeyState(VK_CAPITAL) & 0x0001) {
+ state |= LockMask;
+ }
+ if (GetKeyState(VK_NUMLOCK) & 0x0001) {
+ state |= Mod1Mask;
+ }
+ if (GetKeyState(VK_SCROLL) & 0x0001) {
+ state |= Mod3Mask;
+ }
+ if (GetKeyState(VK_LBUTTON) & 0x8000) {
+ state |= Button1Mask;
+ }
+ if (GetKeyState(VK_MBUTTON) & 0x8000) {
+ state |= Button2Mask;
+ }
+ if (GetKeyState(VK_RBUTTON) & 0x8000) {
+ state |= Button3Mask;
+ }
+ return state;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_PointerEvent --
+ *
+ * This procedure is called for each pointer-related event.
+ * It converts the position to root coords and updates the
+ * global pointer state machine. It also ensures that the
+ * mouse timer is scheduled.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May queue events and change the grab state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_PointerEvent(hwnd, x, y)
+ HWND hwnd; /* Window for coords, or NULL for
+ * the root window. */
+ int x, y; /* Coords relative to hwnd, or screen
+ * if hwnd is NULL. */
+{
+ POINT pos;
+ int state;
+ Tk_Window tkwin;
+
+ pos.x = x;
+ pos.y = y;
+
+ /*
+ * Convert client coords to root coords if we were given a window.
+ */
+
+ if (hwnd) {
+ ClientToScreen(hwnd, &pos);
+ }
+
+ /*
+ * If the mouse is captured, Windows will report all pointer
+ * events to the capture window. So, we need to determine which
+ * window the mouse is really over and change the event. Note
+ * that the computed hwnd may point to a window not owned by Tk,
+ * or a toplevel decorative frame, so tkwin can be NULL.
+ */
+
+ if (captured || hwnd == NULL) {
+ hwnd = WindowFromPoint(pos);
+ }
+ tkwin = Tk_HWNDToWindow(hwnd);
+
+ state = TkWinGetModifierState();
+
+ Tk_UpdatePointer(tkwin, pos.x, pos.y, state);
+
+ if ((captured || tkwin) && !mouseTimerSet) {
+ mouseTimerSet = 1;
+ mouseTimer = Tcl_CreateTimerHandler(MOUSE_TIMER_INTERVAL,
+ MouseTimerProc, NULL);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGrabKeyboard --
+ *
+ * Simulates a keyboard grab by setting the focus.
+ *
+ * Results:
+ * Always returns GrabSuccess.
+ *
+ * Side effects:
+ * Sets the keyboard focus to the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+XGrabKeyboard(display, grab_window, owner_events, pointer_mode,
+ keyboard_mode, time)
+ Display* display;
+ Window grab_window;
+ Bool owner_events;
+ int pointer_mode;
+ int keyboard_mode;
+ Time time;
+{
+ keyboardWinPtr = TkWinGetWinPtr(grab_window);
+ return GrabSuccess;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XUngrabKeyboard --
+ *
+ * Releases the simulated keyboard grab.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets the keyboard focus back to the value before the grab.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XUngrabKeyboard(display, time)
+ Display* display;
+ Time time;
+{
+ keyboardWinPtr = NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MouseTimerProc --
+ *
+ * Check the current mouse position and look for enter/leave
+ * events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May schedule a new timer and/or generate enter/leave events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+MouseTimerProc(clientData)
+ ClientData clientData;
+{
+ POINT pos;
+
+ mouseTimerSet = 0;
+
+ /*
+ * Get the current mouse position and window. Don't do anything
+ * if the mouse hasn't moved since the last time we looked.
+ */
+
+ GetCursorPos(&pos);
+ Tk_PointerEvent(NULL, pos.x, pos.y);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetPointerCoords --
+ *
+ * Fetch the position of the mouse pointer.
+ *
+ * Results:
+ * *xPtr and *yPtr are filled in with the root coordinates
+ * of the mouse pointer for the display.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkGetPointerCoords(tkwin, xPtr, yPtr)
+ Tk_Window tkwin; /* Window that identifies screen on which
+ * lookup is to be done. */
+ int *xPtr, *yPtr; /* Store pointer coordinates here. */
+{
+ POINT point;
+
+ GetCursorPos(&point);
+ *xPtr = point.x;
+ *yPtr = point.y;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XQueryPointer --
+ *
+ * Check the current state of the mouse. This is not a complete
+ * implementation of this function. It only computes the root
+ * coordinates and the current mask.
+ *
+ * Results:
+ * Sets root_x_return, root_y_return, and mask_return. Returns
+ * true on success.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Bool
+XQueryPointer(display, w, root_return, child_return, root_x_return,
+ root_y_return, win_x_return, win_y_return, mask_return)
+ Display* display;
+ Window w;
+ Window* root_return;
+ Window* child_return;
+ int* root_x_return;
+ int* root_y_return;
+ int* win_x_return;
+ int* win_y_return;
+ unsigned int* mask_return;
+{
+ display->request++;
+ TkGetPointerCoords(NULL, root_x_return, root_y_return);
+ *mask_return = TkWinGetModifierState();
+ return True;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XGetInputFocus --
+ *
+ * Retrieves the current keyboard focus window.
+ *
+ * Results:
+ * Returns the current focus window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XGetInputFocus(display, focus_return, revert_to_return)
+ Display *display;
+ Window *focus_return;
+ int *revert_to_return;
+{
+ Tk_Window tkwin = Tk_HWNDToWindow(GetFocus());
+ *focus_return = tkwin ? Tk_WindowId(tkwin) : None;
+ *revert_to_return = RevertToParent;
+ display->request++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XSetInputFocus --
+ *
+ * Set the current focus window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the keyboard focus and causes the selected window to
+ * be activated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XSetInputFocus(display, focus, revert_to, time)
+ Display* display;
+ Window focus;
+ int revert_to;
+ Time time;
+{
+ display->request++;
+ if (focus != None) {
+ SetFocus(Tk_GetHWND(focus));
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpChangeFocus --
+ *
+ * This procedure is invoked to move the system focus from
+ * one window to another.
+ *
+ * Results:
+ * The return value is the serial number of the command that
+ * changed the focus. It may be needed by the caller to filter
+ * out focus change events that were queued before the command.
+ * If the procedure doesn't actually change the focus then
+ * it returns 0.
+ *
+ * Side effects:
+ * The official Windows focus window changes; the application's focus
+ * window isn't changed by this procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpChangeFocus(winPtr, force)
+ TkWindow *winPtr; /* Window that is to receive the X focus. */
+ int force; /* Non-zero means claim the focus even
+ * if it didn't originally belong to
+ * topLevelPtr's application. */
+{
+ TkDisplay *dispPtr = winPtr->dispPtr;
+ Window focusWindow;
+ int dummy, serial;
+ TkWindow *winPtr2;
+
+ if (!force) {
+ XGetInputFocus(dispPtr->display, &focusWindow, &dummy);
+ winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow);
+ if ((winPtr2 == NULL) || (winPtr2->mainPtr != winPtr->mainPtr)) {
+ return 0;
+ }
+ }
+
+ if (winPtr->window == None) {
+ panic("ChangeXFocus got null X window");
+ }
+ XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent,
+ CurrentTime);
+
+ /*
+ * Remember the current serial number for the X server and issue
+ * a dummy server request. This marks the position at which we
+ * changed the focus, so we can distinguish FocusIn and FocusOut
+ * events on either side of the mark.
+ */
+
+ serial = NextRequest(winPtr->display);
+ XNoOp(winPtr->display);
+ return serial;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetCapture --
+ *
+ * This function captures the mouse so that all future events
+ * will be reported to this window, even if the mouse is outside
+ * the window. If the specified window is NULL, then the mouse
+ * is released.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets the capture flag and captures the mouse.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetCapture(winPtr)
+ TkWindow *winPtr; /* Capture window, or NULL. */
+{
+ if (winPtr) {
+ SetCapture(Tk_GetHWND(Tk_WindowId(winPtr)));
+ captured = 1;
+ } else {
+ captured = 0;
+ ReleaseCapture();
+ }
+}
diff --git a/win/tkWinPort.h b/win/tkWinPort.h
new file mode 100644
index 0000000..1f755a7
--- /dev/null
+++ b/win/tkWinPort.h
@@ -0,0 +1,117 @@
+/*
+ * tkWinPort.h --
+ *
+ * This header file handles porting issues that occur because of
+ * differences between Windows and Unix. It should be the only
+ * file that contains #ifdefs to handle different flavors of OS.
+ *
+ * Copyright (c) 1995-1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinPort.h 1.25 97/04/21 17:08:42
+ */
+
+#ifndef _WINPORT
+#define _WINPORT
+
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#include <malloc.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#ifdef _MSC_VER
+# define hypot _hypot
+#endif /* _MSC_VER */
+
+#define strncasecmp strnicmp
+#define strcasecmp stricmp
+
+#define NBBY 8
+
+#define OPEN_MAX 32
+
+/*
+ * The following define causes Tk to use its internal keysym hash table
+ */
+
+#define REDO_KEYSYM_LOOKUP
+
+/*
+ * The following macro checks to see whether there is buffered
+ * input data available for a stdio FILE.
+ */
+
+#ifdef _MSC_VER
+# define TK_READ_DATA_PENDING(f) ((f)->_cnt > 0)
+#else /* _MSC_VER */
+# define TK_READ_DATA_PENDING(f) ((f)->level > 0)
+#endif /* _MSC_VER */
+
+/*
+ * The following stubs implement various calls that don't do anything
+ * under Windows.
+ */
+
+#define TkFreeWindowId(dispPtr,w)
+#define TkInitXId(dispPtr)
+#define TkpCmapStressed(tkwin,colormap) (0)
+#define XFlush(display)
+#define XGrabServer(display)
+#define XUngrabServer(display)
+#define TkpSync(display)
+
+/*
+ * The following functions are implemented as macros under Windows.
+ */
+
+#define XFree(data) {if ((data) != NULL) ckfree((char *) (data));}
+#define XNoOp(display) {display->request++;}
+#define XSynchronize(display, bool) {display->request++;}
+#define XSync(display, bool) {display->request++;}
+#define XVisualIDFromVisual(visual) (visual->visualid)
+
+/*
+ * The following Tk functions are implemented as macros under Windows.
+ */
+
+#define TkGetNativeProlog(interp) TkGetProlog(interp)
+#define TkpGetPixel(p) (((((p)->red >> 8) & 0xff) \
+ | ((p)->green & 0xff00) | (((p)->blue << 8) & 0xff0000)) | 0x20000000)
+
+/*
+ * These calls implement native bitmaps which are not currently
+ * supported under Windows. The macros eliminate the calls.
+ */
+
+#define TkpDefineNativeBitmaps()
+#define TkpCreateNativeBitmap(display, source) None
+#define TkpGetNativeAppBitmap(display, name, w, h) None
+
+/*
+ * Define timezone for gettimeofday.
+ */
+
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+
+extern int gettimeofday(struct timeval *, struct timezone *);
+EXTERN void panic _ANSI_ARGS_(TCL_VARARGS(char *,format));
+
+#endif /* _WINPORT */
diff --git a/win/tkWinRegion.c b/win/tkWinRegion.c
new file mode 100644
index 0000000..3303022
--- /dev/null
+++ b/win/tkWinRegion.c
@@ -0,0 +1,179 @@
+/*
+ * tkWinRegion.c --
+ *
+ * Tk Region emulation code.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinRegion.c 1.7 96/05/03 11:05:54
+ */
+
+#include "tkWinInt.h"
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCreateRegion --
+ *
+ * Construct an empty region.
+ *
+ * Results:
+ * Returns a new region handle.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkRegion
+TkCreateRegion()
+{
+ RECT rect;
+ memset(&rect, 0, sizeof(RECT));
+ return (TkRegion) CreateRectRgnIndirect(&rect);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkDestroyRegion --
+ *
+ * Destroy the specified region.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees the storage associated with the specified region.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkDestroyRegion(r)
+ TkRegion r;
+{
+ DeleteObject((HRGN) r);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkClipBox --
+ *
+ * Computes the bounding box of a region.
+ *
+ * Results:
+ * Sets rect_return to the bounding box of the region.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkClipBox(r, rect_return)
+ TkRegion r;
+ XRectangle* rect_return;
+{
+ RECT rect;
+ GetRgnBox((HRGN)r, &rect);
+ rect_return->x = (short) rect.left;
+ rect_return->y = (short) rect.top;
+ rect_return->width = (short) (rect.right - rect.left);
+ rect_return->height = (short) (rect.bottom - rect.top);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkIntersectRegion --
+ *
+ * Compute the intersection of two regions.
+ *
+ * Results:
+ * Returns the result in the dr_return region.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkIntersectRegion(sra, srb, dr_return)
+ TkRegion sra;
+ TkRegion srb;
+ TkRegion dr_return;
+{
+ CombineRgn((HRGN) dr_return, (HRGN) sra, (HRGN) srb, RGN_AND);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUnionRectWithRegion --
+ *
+ * Create the union of a source region and a rectangle.
+ *
+ * Results:
+ * Returns the result in the dr_return region.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkUnionRectWithRegion(rectangle, src_region, dest_region_return)
+ XRectangle* rectangle;
+ TkRegion src_region;
+ TkRegion dest_region_return;
+{
+ HRGN rectRgn = CreateRectRgn(rectangle->x, rectangle->y,
+ rectangle->x + rectangle->width, rectangle->y + rectangle->height);
+ CombineRgn((HRGN) dest_region_return, (HRGN) src_region,
+ (HRGN) rectRgn, RGN_OR);
+ DeleteObject(rectRgn);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkRectInRegion --
+ *
+ * Test whether a given rectangle overlaps with a region.
+ *
+ * Results:
+ * Returns RectanglePart or RectangleOut. Note that this is
+ * not a complete implementation since it doesn't test for
+ * RectangleIn.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkRectInRegion(r, x, y, width, height)
+ TkRegion r;
+ int x;
+ int y;
+ unsigned int width;
+ unsigned int height;
+{
+ RECT rect;
+ rect.top = y;
+ rect.left = x;
+ rect.bottom = y+height;
+ rect.right = x+width;
+ return RectInRegion((HRGN)r, &rect) ? RectanglePart : RectangleOut;
+}
diff --git a/win/tkWinScrlbr.c b/win/tkWinScrlbr.c
new file mode 100644
index 0000000..6c1a664
--- /dev/null
+++ b/win/tkWinScrlbr.c
@@ -0,0 +1,745 @@
+/*
+ * tkWinScrollbar.c --
+ *
+ * This file implements the Windows specific portion of the scrollbar
+ * widget.
+ *
+ * Copyright (c) 1996 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinScrlbr.c 1.19 97/08/13 17:37:49
+ */
+
+#include "tkWinInt.h"
+#include "tkScrollbar.h"
+
+
+/*
+ * The following constant is used to specify the maximum scroll position.
+ * This value is limited by the Win32 API to either 16-bits or 32-bits,
+ * depending on the context. For now we'll just use a value small
+ * enough to fit in 16-bits, but which gives us 4-digits of precision.
+ */
+
+#define MAX_SCROLL 10000
+
+/*
+ * Declaration of Windows specific scrollbar structure.
+ */
+
+typedef struct WinScrollbar {
+ TkScrollbar info; /* Generic scrollbar info. */
+ WNDPROC oldProc; /* Old window procedure. */
+ int lastVertical; /* 1 if was vertical at last refresh. */
+ HWND hwnd; /* Current window handle. */
+ int winFlags; /* Various flags; see below. */
+} WinScrollbar;
+
+/*
+ * Flag bits for native scrollbars:
+ *
+ * IN_MODAL_LOOP: Non-zero means this scrollbar is in the middle
+ * of a modal loop.
+ * ALREADY_DEAD: Non-zero means this scrollbar has been
+ * destroyed, but has not been cleaned up.
+ */
+
+#define IN_MODAL_LOOP 1
+#define ALREADY_DEAD 2
+
+/*
+ * Cached system metrics used to determine scrollbar geometry.
+ */
+
+static int initialized = 0;
+static int hArrowWidth, hThumb; /* Horizontal control metrics. */
+static int vArrowWidth, vArrowHeight, vThumb; /* Vertical control metrics. */
+
+/*
+ * This variable holds the default width for a scrollbar in string
+ * form for use in a Tk_ConfigSpec.
+ */
+
+static char defWidth[8];
+
+/*
+ * Declarations for functions defined in this file.
+ */
+
+static Window CreateProc _ANSI_ARGS_((Tk_Window tkwin,
+ Window parent, ClientData instanceData));
+static void ModalLoopProc _ANSI_ARGS_((Tk_Window tkwin,
+ XEvent *eventPtr));
+static int ScrollbarBindProc _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, XEvent *eventPtr,
+ Tk_Window tkwin, KeySym keySym));
+static LRESULT CALLBACK ScrollbarProc _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+static void UpdateScrollbar _ANSI_ARGS_((
+ WinScrollbar *scrollPtr));
+static void UpdateScrollbarMetrics _ANSI_ARGS_((void));
+
+/*
+ * The class procedure table for the scrollbar widget.
+ */
+
+TkClassProcs tkpScrollbarProcs = {
+ CreateProc, /* createProc */
+ NULL, /* geometryProc */
+ ModalLoopProc, /* modalProc */
+};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateScrollbar --
+ *
+ * Allocate a new TkScrollbar structure.
+ *
+ * Results:
+ * Returns a newly allocated TkScrollbar structure.
+ *
+ * Side effects:
+ * Registers an event handler for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkScrollbar *
+TkpCreateScrollbar(tkwin)
+ Tk_Window tkwin;
+{
+ WinScrollbar *scrollPtr;
+ TkWindow *winPtr = (TkWindow *)tkwin;
+
+ if (!initialized) {
+ UpdateScrollbarMetrics();
+ initialized = 1;
+ }
+
+ scrollPtr = (WinScrollbar *) ckalloc(sizeof(WinScrollbar));
+ scrollPtr->winFlags = 0;
+ scrollPtr->hwnd = NULL;
+
+ Tk_CreateEventHandler(tkwin,
+ ExposureMask|StructureNotifyMask|FocusChangeMask,
+ TkScrollbarEventProc, (ClientData) scrollPtr);
+
+ if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) {
+ Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL,
+ (ClientData)1);
+ TkCreateBindingProcedure(winPtr->mainPtr->interp,
+ winPtr->mainPtr->bindingTable,
+ (ClientData)Tk_GetUid("Scrollbar"), "<ButtonPress>",
+ ScrollbarBindProc, NULL, NULL);
+ }
+
+ return (TkScrollbar*) scrollPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateScrollbar --
+ *
+ * This function updates the position and size of the scrollbar
+ * thumb based on the current settings.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Moves the thumb.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateScrollbar(scrollPtr)
+ WinScrollbar *scrollPtr;
+{
+ SCROLLINFO scrollInfo;
+ double thumbSize;
+
+ /*
+ * Update the current scrollbar position and shape.
+ */
+
+ scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
+ scrollInfo.cbSize = sizeof(scrollInfo);
+ scrollInfo.nMin = 0;
+ scrollInfo.nMax = MAX_SCROLL;
+ thumbSize = (scrollPtr->info.lastFraction - scrollPtr->info.firstFraction);
+ if (tkpIsWin32s) {
+ scrollInfo.nPage = 0;
+ } else {
+ scrollInfo.nPage = ((UINT) (thumbSize * (double) MAX_SCROLL)) + 1;
+ }
+ if (thumbSize < 1.0) {
+ scrollInfo.nPos = (int)
+ ((scrollPtr->info.firstFraction / (1.0-thumbSize))
+ * (MAX_SCROLL - (scrollInfo.nPage - 1)));
+ } else {
+ scrollInfo.nPos = 0;
+ }
+ SetScrollInfo(scrollPtr->hwnd, SB_CTL, &scrollInfo, TRUE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateProc --
+ *
+ * This function creates a new Scrollbar control, subclasses
+ * the instance, and generates a new Window object.
+ *
+ * Results:
+ * Returns the newly allocated Window object, or None on failure.
+ *
+ * Side effects:
+ * Causes a new Scrollbar control to come into existence.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Window
+CreateProc(tkwin, parentWin, instanceData)
+ Tk_Window tkwin; /* Token for window. */
+ Window parentWin; /* Parent of new window. */
+ ClientData instanceData; /* Scrollbar instance data. */
+{
+ DWORD style;
+ Window window;
+ HWND parent;
+ TkWindow *winPtr;
+ WinScrollbar *scrollPtr = (WinScrollbar *)instanceData;
+
+ parent = Tk_GetHWND(parentWin);
+
+ if (scrollPtr->info.vertical) {
+ style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
+ | SBS_VERT | SBS_RIGHTALIGN;
+ } else {
+ style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
+ | SBS_HORZ | SBS_BOTTOMALIGN;
+ }
+
+ scrollPtr->hwnd = CreateWindow("SCROLLBAR", NULL, style,
+ Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
+ parent, NULL, Tk_GetHINSTANCE(), NULL);
+
+ /*
+ * Ensure new window is inserted into the stacking order at the correct
+ * place.
+ */
+
+ SetWindowPos(scrollPtr->hwnd, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+
+ for (winPtr = ((TkWindow*)tkwin)->nextPtr; winPtr != NULL;
+ winPtr = winPtr->nextPtr) {
+ if ((winPtr->window != None) && !(winPtr->flags & TK_TOP_LEVEL)) {
+ TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window),
+ Below);
+ break;
+ }
+ }
+
+ scrollPtr->lastVertical = scrollPtr->info.vertical;
+ scrollPtr->oldProc = (WNDPROC)SetWindowLong(scrollPtr->hwnd, GWL_WNDPROC,
+ (DWORD) ScrollbarProc);
+ window = Tk_AttachHWND(tkwin, scrollPtr->hwnd);
+
+ UpdateScrollbar(scrollPtr);
+ return window;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpDisplayScrollbar --
+ *
+ * This procedure redraws the contents of a scrollbar window.
+ * It is invoked as a do-when-idle handler, so it only runs
+ * when there's nothing else for the application to do.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpDisplayScrollbar(clientData)
+ ClientData clientData; /* Information about window. */
+{
+ WinScrollbar *scrollPtr = (WinScrollbar *) clientData;
+ Tk_Window tkwin = scrollPtr->info.tkwin;
+
+ scrollPtr->info.flags &= ~REDRAW_PENDING;
+ if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+
+ /*
+ * Destroy and recreate the scrollbar control if the orientation
+ * has changed.
+ */
+
+ if (scrollPtr->lastVertical != scrollPtr->info.vertical) {
+ HWND hwnd = Tk_GetHWND(Tk_WindowId(tkwin));
+
+ SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) scrollPtr->oldProc);
+ DestroyWindow(hwnd);
+
+ CreateProc(tkwin, Tk_WindowId(Tk_Parent(tkwin)),
+ (ClientData) scrollPtr);
+ } else {
+ UpdateScrollbar(scrollPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyScrollbar --
+ *
+ * Free data structures associated with the scrollbar control.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Restores the default control state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyScrollbar(scrollPtr)
+ TkScrollbar *scrollPtr;
+{
+ WinScrollbar *winScrollPtr = (WinScrollbar *)scrollPtr;
+ HWND hwnd = winScrollPtr->hwnd;
+ if (hwnd) {
+ SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winScrollPtr->oldProc);
+ if (winScrollPtr->winFlags & IN_MODAL_LOOP) {
+ ((TkWindow *)scrollPtr->tkwin)->flags |= TK_DONT_DESTROY_WINDOW;
+ SetParent(hwnd, NULL);
+ }
+ }
+ winScrollPtr->winFlags |= ALREADY_DEAD;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateScrollbarMetrics --
+ *
+ * This function retrieves the current system metrics for a
+ * scrollbar.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Updates the geometry cache info for all scrollbars.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+UpdateScrollbarMetrics()
+{
+ Tk_ConfigSpec *specPtr;
+
+ hArrowWidth = GetSystemMetrics(SM_CXHSCROLL);
+ hThumb = GetSystemMetrics(SM_CXHTHUMB);
+ vArrowWidth = GetSystemMetrics(SM_CXVSCROLL);
+ vArrowHeight = GetSystemMetrics(SM_CYVSCROLL);
+ vThumb = GetSystemMetrics(SM_CYVTHUMB);
+
+ sprintf(defWidth, "%d", vArrowWidth);
+ for (specPtr = tkpScrollbarConfigSpecs; specPtr->type != TK_CONFIG_END;
+ specPtr++) {
+ if (specPtr->offset == Tk_Offset(TkScrollbar, width)) {
+ specPtr->defValue = defWidth;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpComputeScrollbarGeometry --
+ *
+ * After changes in a scrollbar's size or configuration, this
+ * procedure recomputes various geometry information used in
+ * displaying the scrollbar.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The scrollbar will be displayed differently.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpComputeScrollbarGeometry(scrollPtr)
+ register TkScrollbar *scrollPtr; /* Scrollbar whose geometry may
+ * have changed. */
+{
+ int fieldLength, minThumbSize;
+
+ /*
+ * Windows doesn't use focus rings on scrollbars, but we still
+ * perform basic sanity checks to appease backwards compatibility.
+ */
+
+ if (scrollPtr->highlightWidth < 0) {
+ scrollPtr->highlightWidth = 0;
+ }
+
+ if (scrollPtr->vertical) {
+ scrollPtr->arrowLength = vArrowHeight;
+ fieldLength = Tk_Height(scrollPtr->tkwin);
+ minThumbSize = vThumb;
+ } else {
+ scrollPtr->arrowLength = hArrowWidth;
+ fieldLength = Tk_Width(scrollPtr->tkwin);
+ minThumbSize = hThumb;
+ }
+ fieldLength -= 2*scrollPtr->arrowLength;
+ if (fieldLength < 0) {
+ fieldLength = 0;
+ }
+ scrollPtr->sliderFirst = (int) ((double)fieldLength
+ * scrollPtr->firstFraction);
+ scrollPtr->sliderLast = (int) ((double)fieldLength
+ * scrollPtr->lastFraction);
+
+ /*
+ * Adjust the slider so that some piece of it is always
+ * displayed in the scrollbar and so that it has at least
+ * a minimal width (so it can be grabbed with the mouse).
+ */
+
+ if (scrollPtr->sliderFirst > fieldLength) {
+ scrollPtr->sliderFirst = fieldLength;
+ }
+ if (scrollPtr->sliderFirst < 0) {
+ scrollPtr->sliderFirst = 0;
+ }
+ if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
+ + minThumbSize)) {
+ scrollPtr->sliderLast = scrollPtr->sliderFirst + minThumbSize;
+ }
+ if (scrollPtr->sliderLast > fieldLength) {
+ scrollPtr->sliderLast = fieldLength;
+ }
+ scrollPtr->sliderFirst += scrollPtr->arrowLength;
+ scrollPtr->sliderLast += scrollPtr->arrowLength;
+
+ /*
+ * Register the desired geometry for the window (leave enough space
+ * for the two arrows plus a minimum-size slider, plus border around
+ * the whole window, if any). Then arrange for the window to be
+ * redisplayed.
+ */
+
+ if (scrollPtr->vertical) {
+ Tk_GeometryRequest(scrollPtr->tkwin,
+ scrollPtr->width, 2*scrollPtr->arrowLength + minThumbSize);
+ } else {
+ Tk_GeometryRequest(scrollPtr->tkwin,
+ 2*scrollPtr->arrowLength + minThumbSize, scrollPtr->width);
+ }
+ Tk_SetInternalBorder(scrollPtr->tkwin, 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollbarProc --
+ *
+ * This function is call by Windows whenever an event occurs on
+ * a scrollbar control created by Tk.
+ *
+ * Results:
+ * Standard Windows return value.
+ *
+ * Side effects:
+ * May generate events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static LRESULT CALLBACK
+ScrollbarProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ LRESULT result;
+ POINT point;
+ WinScrollbar *scrollPtr;
+ Tk_Window tkwin = Tk_HWNDToWindow(hwnd);
+
+ if (tkwin == NULL) {
+ panic("ScrollbarProc called on an invalid HWND");
+ }
+ scrollPtr = (WinScrollbar *)((TkWindow*)tkwin)->instanceData;
+
+ switch(message) {
+ case WM_HSCROLL:
+ case WM_VSCROLL: {
+ Tcl_Interp *interp;
+ Tcl_DString cmdString;
+ int command = LOWORD(wParam);
+ int code;
+
+ GetCursorPos(&point);
+ Tk_TranslateWinEvent(NULL, WM_MOUSEMOVE, 0,
+ MAKELPARAM(point.x, point.y), &result);
+
+ if (command == SB_ENDSCROLL) {
+ return 0;
+ }
+
+ /*
+ * Bail out immediately if there isn't a command to invoke.
+ */
+
+ if (scrollPtr->info.commandSize == 0) {
+ Tcl_ServiceAll();
+ return 0;
+ }
+
+ Tcl_DStringInit(&cmdString);
+ Tcl_DStringAppend(&cmdString, scrollPtr->info.command,
+ scrollPtr->info.commandSize);
+
+ if (command == SB_LINELEFT || command == SB_LINERIGHT) {
+ Tcl_DStringAppendElement(&cmdString, "scroll");
+ Tcl_DStringAppendElement(&cmdString,
+ (command == SB_LINELEFT ) ? "-1" : "1");
+ Tcl_DStringAppendElement(&cmdString, "units");
+ } else if (command == SB_PAGELEFT || command == SB_PAGERIGHT) {
+ Tcl_DStringAppendElement(&cmdString, "scroll");
+ Tcl_DStringAppendElement(&cmdString,
+ (command == SB_PAGELEFT ) ? "-1" : "1");
+ Tcl_DStringAppendElement(&cmdString, "pages");
+ } else {
+ char valueString[TCL_DOUBLE_SPACE];
+ double pos = 0.0;
+ switch (command) {
+ case SB_THUMBPOSITION:
+ pos = ((double)HIWORD(wParam)) / MAX_SCROLL;
+ break;
+
+ case SB_THUMBTRACK:
+ pos = ((double)HIWORD(wParam)) / MAX_SCROLL;
+ break;
+
+ case SB_TOP:
+ pos = 0.0;
+ break;
+
+ case SB_BOTTOM:
+ pos = 1.0;
+ break;
+ }
+ sprintf(valueString, "%g", pos);
+ Tcl_DStringAppendElement(&cmdString, "moveto");
+ Tcl_DStringAppendElement(&cmdString, valueString);
+ }
+
+ interp = scrollPtr->info.interp;
+ code = Tcl_GlobalEval(interp, cmdString.string);
+ if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) {
+ Tcl_AddErrorInfo(interp, "\n (scrollbar command)");
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_DStringFree(&cmdString);
+
+ Tcl_ServiceAll();
+ return 0;
+ }
+
+ default:
+ if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) {
+ return result;
+ }
+ }
+ return CallWindowProc(scrollPtr->oldProc, hwnd, message, wParam, lParam);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpConfigureScrollbar --
+ *
+ * This procedure is called after the generic code has finished
+ * processing configuration options, in order to configure
+ * platform specific options.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpConfigureScrollbar(scrollPtr)
+ register TkScrollbar *scrollPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+{
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ScrollbarBindProc --
+ *
+ * This procedure is invoked when the default <ButtonPress>
+ * binding on the Scrollbar bind tag fires.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The event enters a modal loop.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ScrollbarBindProc(clientData, interp, eventPtr, tkwin, keySym)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ XEvent *eventPtr;
+ Tk_Window tkwin;
+ KeySym keySym;
+{
+ TkWindow *winPtr = (TkWindow*)tkwin;
+ if (eventPtr->type == ButtonPress) {
+ winPtr->flags |= TK_DEFER_MODAL;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ModalLoopProc --
+ *
+ * This function is invoked at the end of the event processing
+ * whenever the ScrollbarBindProc has been invoked for a ButtonPress
+ * event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Enters a modal loop.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ModalLoopProc(tkwin, eventPtr)
+ Tk_Window tkwin;
+ XEvent *eventPtr;
+{
+ TkWindow *winPtr = (TkWindow*)tkwin;
+ WinScrollbar *scrollPtr = (WinScrollbar *) winPtr->instanceData;
+ int oldMode;
+
+ Tcl_Preserve((ClientData)scrollPtr);
+ scrollPtr->winFlags |= IN_MODAL_LOOP;
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ TkWinResendEvent(scrollPtr->oldProc, scrollPtr->hwnd, eventPtr);
+ (void) Tcl_SetServiceMode(oldMode);
+ scrollPtr->winFlags &= ~IN_MODAL_LOOP;
+ if (scrollPtr->hwnd && scrollPtr->winFlags & ALREADY_DEAD) {
+ DestroyWindow(scrollPtr->hwnd);
+ }
+ Tcl_Release((ClientData)scrollPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpScrollbarPosition --
+ *
+ * Determine the scrollbar element corresponding to a
+ * given position.
+ *
+ * Results:
+ * One of TOP_ARROW, TOP_GAP, etc., indicating which element
+ * of the scrollbar covers the position given by (x, y). If
+ * (x,y) is outside the scrollbar entirely, then OUTSIDE is
+ * returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+TkpScrollbarPosition(scrollPtr, x, y)
+ register TkScrollbar *scrollPtr; /* Scrollbar widget record. */
+ int x, y; /* Coordinates within scrollPtr's
+ * window. */
+{
+ int length, width, tmp;
+
+ if (scrollPtr->vertical) {
+ length = Tk_Height(scrollPtr->tkwin);
+ width = Tk_Width(scrollPtr->tkwin);
+ } else {
+ tmp = x;
+ x = y;
+ y = tmp;
+ length = Tk_Width(scrollPtr->tkwin);
+ width = Tk_Height(scrollPtr->tkwin);
+ }
+
+ if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
+ || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
+ return OUTSIDE;
+ }
+
+ /*
+ * All of the calculations in this procedure mirror those in
+ * TkpDisplayScrollbar. Be sure to keep the two consistent.
+ */
+
+ if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
+ return TOP_ARROW;
+ }
+ if (y < scrollPtr->sliderFirst) {
+ return TOP_GAP;
+ }
+ if (y < scrollPtr->sliderLast) {
+ return SLIDER;
+ }
+ if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
+ return BOTTOM_ARROW;
+ }
+ return BOTTOM_GAP;
+}
diff --git a/win/tkWinSend.c b/win/tkWinSend.c
new file mode 100644
index 0000000..6d12ed4
--- /dev/null
+++ b/win/tkWinSend.c
@@ -0,0 +1,86 @@
+/*
+ * tkWinSend.c --
+ *
+ * This file provides procedures that implement the "send"
+ * command, allowing commands to be passed from interpreter
+ * to interpreter.
+ *
+ * Copyright (c) 1997 by Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinSend.c 1.4 97/06/10 09:39:50
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_SetAppName --
+ *
+ * This procedure is called to associate an ASCII name with a Tk
+ * application. If the application has already been named, the
+ * name replaces the old one.
+ *
+ * Results:
+ * The return value is the name actually given to the application.
+ * This will normally be the same as name, but if name was already
+ * in use for an application then a name of the form "name #2" will
+ * be chosen, with a high enough number to make the name unique.
+ *
+ * Side effects:
+ * Registration info is saved, thereby allowing the "send" command
+ * to be used later to invoke commands in the application. In
+ * addition, the "send" command is created in the application's
+ * interpreter. The registration will be removed automatically
+ * if the interpreter is deleted or the "send" command is removed.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+Tk_SetAppName(tkwin, name)
+ Tk_Window tkwin; /* Token for any window in the application
+ * to be named: it is just used to identify
+ * the application and the display. */
+ char *name; /* The name that will be used to
+ * refer to the interpreter in later
+ * "send" commands. Must be globally
+ * unique. */
+{
+ return name;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetInterpNames --
+ *
+ * This procedure is invoked to fetch a list of all the
+ * interpreter names currently registered for the display
+ * of a particular window.
+ *
+ * Results:
+ * A standard Tcl return value. Interp->result will be set
+ * to hold a list of all the interpreter names defined for
+ * tkwin's display. If an error occurs, then TCL_ERROR
+ * is returned and interp->result will hold an error message.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkGetInterpNames(interp, tkwin)
+ Tcl_Interp *interp; /* Interpreter for returning a result. */
+ Tk_Window tkwin; /* Window whose display is to be used
+ * for the lookup. */
+{
+ return TCL_OK;
+}
diff --git a/win/tkWinWindow.c b/win/tkWinWindow.c
new file mode 100644
index 0000000..2b8eb41
--- /dev/null
+++ b/win/tkWinWindow.c
@@ -0,0 +1,796 @@
+/*
+ * tkWinWindow.c --
+ *
+ * Xlib emulation routines for Windows related to creating,
+ * displaying and destroying windows.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinWindow.c 1.23 97/07/01 18:14:13
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * The windowTable maps from HWND to Tk_Window handles.
+ */
+
+static Tcl_HashTable windowTable;
+
+/*
+ * Have statics in this module been initialized?
+ */
+
+static int initialized = 0;
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static void NotifyVisibility _ANSI_ARGS_((XEvent *eventPtr,
+ TkWindow *winPtr));
+static void StackWindow _ANSI_ARGS_((Window w, Window sibling,
+ int stack_mode));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_AttachHWND --
+ *
+ * This function binds an HWND and a reflection procedure to
+ * the specified Tk_Window.
+ *
+ * Results:
+ * Returns an X Window that encapsulates the HWND.
+ *
+ * Side effects:
+ * May allocate a new X Window. Also enters the HWND into the
+ * global window table.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Window
+Tk_AttachHWND(tkwin, hwnd)
+ Tk_Window tkwin;
+ HWND hwnd;
+{
+ int new;
+ Tcl_HashEntry *entryPtr;
+ TkWinDrawable *twdPtr = (TkWinDrawable *) Tk_WindowId(tkwin);
+
+ if (!initialized) {
+ Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS);
+ initialized = 1;
+ }
+
+ /*
+ * Allocate a new drawable if necessary. Otherwise, remove the
+ * previous HWND from from the window table.
+ */
+
+ if (twdPtr == NULL) {
+ twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
+ twdPtr->type = TWD_WINDOW;
+ twdPtr->window.winPtr = (TkWindow *) tkwin;
+ } else if (twdPtr->window.handle != NULL) {
+ entryPtr = Tcl_FindHashEntry(&windowTable,
+ (char *)twdPtr->window.handle);
+ Tcl_DeleteHashEntry(entryPtr);
+ }
+
+ /*
+ * Insert the new HWND into the window table.
+ */
+
+ twdPtr->window.handle = hwnd;
+ entryPtr = Tcl_CreateHashEntry(&windowTable, (char *)hwnd, &new);
+ Tcl_SetHashValue(entryPtr, (ClientData)tkwin);
+
+ return (Window)twdPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_HWNDToWindow --
+ *
+ * This function retrieves a Tk_Window from the window table
+ * given an HWND.
+ *
+ * Results:
+ * Returns the matching Tk_Window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tk_Window
+Tk_HWNDToWindow(hwnd)
+ HWND hwnd;
+{
+ Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&windowTable, (char*)hwnd);
+ if (entryPtr != NULL) {
+ return (Tk_Window) Tcl_GetHashValue(entryPtr);
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetHWND --
+ *
+ * This function extracts the HWND from an X Window.
+ *
+ * Results:
+ * Returns the HWND associated with the Window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HWND
+Tk_GetHWND(window)
+ Window window;
+{
+ TkWinDrawable *twdPtr = (TkWinDrawable *) window;
+ return twdPtr->window.handle;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpPrintWindowId --
+ *
+ * This routine stores the string representation of the
+ * platform dependent window handle for an X Window in the
+ * given buffer.
+ *
+ * Results:
+ * Returns the result in the specified buffer.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpPrintWindowId(buf, window)
+ char *buf; /* Pointer to string large enough to hold
+ * the hex representation of a pointer. */
+ Window window; /* Window to be printed into buffer. */
+{
+ HWND hwnd = (window) ? Tk_GetHWND(window) : 0;
+ sprintf(buf, "0x%x", (unsigned int) hwnd);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpScanWindowId --
+ *
+ * Given a string which represents the platform dependent window
+ * handle, produce the X Window id for the window.
+ *
+ * Results:
+ * The return value is normally TCL_OK; in this case *idPtr
+ * will be set to the X Window id equivalent to string. If
+ * string is improperly formed then TCL_ERROR is returned and
+ * an error message will be left in interp->result. If the
+ * number does not correspond to a Tk Window, then *idPtr will
+ * be set to None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpScanWindowId(interp, string, idPtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting. */
+ char *string; /* String containing a (possibly signed)
+ * integer in a form acceptable to strtol. */
+ int *idPtr; /* Place to store converted result. */
+{
+ int number;
+ Tk_Window tkwin;
+
+ if (Tcl_GetInt(interp, string, &number) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tkwin = Tk_HWNDToWindow((HWND)number);
+ if (tkwin) {
+ *idPtr = Tk_WindowId(tkwin);
+ } else {
+ *idPtr = None;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMakeWindow --
+ *
+ * Creates a Windows window object based on the current attributes
+ * of the specified TkWindow.
+ *
+ * Results:
+ * Returns a pointer to a new TkWinDrawable cast to a Window.
+ *
+ * Side effects:
+ * Creates a new window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Window
+TkpMakeWindow(winPtr, parent)
+ TkWindow *winPtr;
+ Window parent;
+{
+ HWND parentWin;
+ int style;
+ HWND hwnd;
+
+ if (parent != None) {
+ parentWin = Tk_GetHWND(parent);
+ style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+ } else {
+ parentWin = NULL;
+ style = WS_POPUP | WS_CLIPCHILDREN;
+ }
+
+ /*
+ * Create the window, then ensure that it is at the top of the
+ * stacking order.
+ */
+
+ hwnd = CreateWindow(TK_WIN_CHILD_CLASS_NAME, NULL, style,
+ Tk_X(winPtr), Tk_Y(winPtr), Tk_Width(winPtr), Tk_Height(winPtr),
+ parentWin, NULL, Tk_GetHINSTANCE(), NULL);
+ SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ return Tk_AttachHWND((Tk_Window)winPtr, hwnd);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XDestroyWindow --
+ *
+ * Destroys the given window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sends the WM_DESTROY message to the window and then destroys
+ * it the Win32 resources associated with the window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XDestroyWindow(display, w)
+ Display* display;
+ Window w;
+{
+ Tcl_HashEntry *entryPtr;
+ TkWinDrawable *twdPtr = (TkWinDrawable *)w;
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+ HWND hwnd = Tk_GetHWND(w);
+
+ display->request++;
+
+ /*
+ * Remove references to the window in the pointer module then
+ * release the drawable.
+ */
+
+ TkPointerDeadWindow(winPtr);
+
+ entryPtr = Tcl_FindHashEntry(&windowTable, (char*)hwnd);
+ if (entryPtr != NULL) {
+ Tcl_DeleteHashEntry(entryPtr);
+ }
+
+ ckfree((char *)twdPtr);
+
+ /*
+ * Don't bother destroying the window if we are going to destroy
+ * the parent later.
+ */
+
+ if (hwnd != NULL && !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) {
+ DestroyWindow(hwnd);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XMapWindow --
+ *
+ * Cause the given window to become visible.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Causes the window state to change, and generates a MapNotify
+ * event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XMapWindow(display, w)
+ Display* display;
+ Window w;
+{
+ XEvent event;
+ TkWindow *parentPtr;
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+
+ display->request++;
+
+ ShowWindow(TkWinGetHWND(w), SW_SHOWNORMAL);
+ winPtr->flags |= TK_MAPPED;
+
+ /*
+ * Check to see if this window is visible now. If all of the parent
+ * windows up to the first toplevel are mapped, then this window and
+ * its mapped children have just become visible.
+ */
+
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ for (parentPtr = winPtr->parentPtr; ;
+ parentPtr = parentPtr->parentPtr) {
+ if ((parentPtr == NULL) || !(parentPtr->flags & TK_MAPPED)) {
+ return;
+ }
+ if (parentPtr->flags & TK_TOP_LEVEL) {
+ break;
+ }
+ }
+ } else {
+ event.type = MapNotify;
+ event.xmap.serial = display->request;
+ event.xmap.send_event = False;
+ event.xmap.display = display;
+ event.xmap.event = winPtr->window;
+ event.xmap.window = winPtr->window;
+ event.xmap.override_redirect = winPtr->atts.override_redirect;
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+ }
+
+ /*
+ * Generate VisibilityNotify events for this window and its mapped
+ * children.
+ */
+
+ event.type = VisibilityNotify;
+ event.xvisibility.serial = display->request;
+ event.xvisibility.send_event = False;
+ event.xvisibility.display = display;
+ event.xvisibility.window = winPtr->window;
+ event.xvisibility.state = VisibilityUnobscured;
+ NotifyVisibility(&event, winPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyVisibility --
+ *
+ * This function recursively notifies the mapped children of the
+ * specified window of a change in visibility. Note that we don't
+ * properly report the visibility state, since Windows does not
+ * provide that info. The eventPtr argument must point to an event
+ * that has been completely initialized except for the window slot.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Generates lots of events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+NotifyVisibility(eventPtr, winPtr)
+ XEvent *eventPtr; /* Initialized VisibilityNotify event. */
+ TkWindow *winPtr; /* Window to notify. */
+{
+ if (winPtr->atts.event_mask & VisibilityChangeMask) {
+ eventPtr->xvisibility.window = winPtr->window;
+ Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
+ }
+ for (winPtr = winPtr->childList; winPtr != NULL;
+ winPtr = winPtr->nextPtr) {
+ if (winPtr->flags & TK_MAPPED) {
+ NotifyVisibility(eventPtr, winPtr);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XUnmapWindow --
+ *
+ * Cause the given window to become invisible.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Causes the window state to change, and generates an UnmapNotify
+ * event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XUnmapWindow(display, w)
+ Display* display;
+ Window w;
+{
+ XEvent event;
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+
+ display->request++;
+
+ /*
+ * Bug fix: Don't short circuit this routine based on TK_MAPPED because
+ * it will be cleared before XUnmapWindow is called.
+ */
+
+ ShowWindow(TkWinGetHWND(w), SW_HIDE);
+ winPtr->flags &= ~TK_MAPPED;
+
+ if (winPtr->flags & TK_TOP_LEVEL) {
+ event.type = UnmapNotify;
+ event.xunmap.serial = display->request;
+ event.xunmap.send_event = False;
+ event.xunmap.display = display;
+ event.xunmap.event = winPtr->window;
+ event.xunmap.window = winPtr->window;
+ event.xunmap.from_configure = False;
+ Tk_HandleEvent(&event);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XMoveResizeWindow --
+ *
+ * Move and resize a window relative to its parent.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Repositions and resizes the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XMoveResizeWindow(display, w, x, y, width, height)
+ Display* display;
+ Window w;
+ int x; /* Position relative to parent. */
+ int y;
+ unsigned int width;
+ unsigned int height;
+{
+ display->request++;
+ MoveWindow(TkWinGetHWND(w), x, y, width, height, TRUE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XMoveWindow --
+ *
+ * Move a window relative to its parent.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Repositions the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XMoveWindow(display, w, x, y)
+ Display* display;
+ Window w;
+ int x;
+ int y;
+{
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+
+ display->request++;
+
+ MoveWindow(TkWinGetHWND(w), x, y, winPtr->changes.width,
+ winPtr->changes.height, TRUE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XResizeWindow --
+ *
+ * Resize a window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Resizes the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XResizeWindow(display, w, width, height)
+ Display* display;
+ Window w;
+ unsigned int width;
+ unsigned int height;
+{
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+
+ display->request++;
+
+ MoveWindow(TkWinGetHWND(w), winPtr->changes.x, winPtr->changes.y, width,
+ height, TRUE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XRaiseWindow --
+ *
+ * Change the stacking order of a window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the stacking order of the specified window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XRaiseWindow(display, w)
+ Display* display;
+ Window w;
+{
+ HWND window = TkWinGetHWND(w);
+
+ display->request++;
+ SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XConfigureWindow --
+ *
+ * Change the size, position, stacking, or border of the specified
+ * window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the attributes of the specified window. Note that we
+ * ignore the passed in values and use the values stored in the
+ * TkWindow data structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XConfigureWindow(display, w, value_mask, values)
+ Display* display;
+ Window w;
+ unsigned int value_mask;
+ XWindowChanges* values;
+{
+ TkWindow *winPtr = TkWinGetWinPtr(w);
+ HWND hwnd = TkWinGetHWND(w);
+
+ display->request++;
+
+ /*
+ * Change the shape and/or position of the window.
+ */
+
+ if (value_mask & (CWX|CWY|CWWidth|CWHeight)) {
+ MoveWindow(hwnd, winPtr->changes.x, winPtr->changes.y,
+ winPtr->changes.width, winPtr->changes.height, TRUE);
+ }
+
+ /*
+ * Change the stacking order of the window.
+ */
+
+ if (value_mask & CWStackMode) {
+ HWND sibling;
+ if ((value_mask & CWSibling) && (values->sibling != None)) {
+ sibling = Tk_GetHWND(values->sibling);
+ } else {
+ sibling = NULL;
+ }
+ TkWinSetWindowPos(hwnd, sibling, values->stack_mode);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XClearWindow --
+ *
+ * Clears the entire window to the current background color.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Erases the current contents of the window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XClearWindow(display, w)
+ Display* display;
+ Window w;
+{
+ RECT rc;
+ HBRUSH brush;
+ HPALETTE oldPalette, palette;
+ TkWindow *winPtr;
+ HWND hwnd = TkWinGetHWND(w);
+ HDC dc = GetDC(hwnd);
+
+ palette = TkWinGetPalette(display->screens[0].cmap);
+ oldPalette = SelectPalette(dc, palette, FALSE);
+
+ display->request++;
+
+ winPtr = TkWinGetWinPtr(w);
+ brush = CreateSolidBrush(winPtr->atts.background_pixel);
+ GetWindowRect(hwnd, &rc);
+ rc.right = rc.right - rc.left;
+ rc.bottom = rc.bottom - rc.top;
+ rc.left = rc.top = 0;
+ FillRect(dc, &rc, brush);
+
+ DeleteObject(brush);
+ SelectPalette(dc, oldPalette, TRUE);
+ ReleaseDC(hwnd, dc);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XChangeWindowAttributes --
+ *
+ * This function is called when the attributes on a window are
+ * updated. Since Tk maintains all of the window state, the only
+ * relevant value is the cursor.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May cause the mouse position to be updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XChangeWindowAttributes(display, w, valueMask, attributes)
+ Display* display;
+ Window w;
+ unsigned long valueMask;
+ XSetWindowAttributes* attributes;
+{
+ if (valueMask & CWCursor) {
+ XDefineCursor(display, w, attributes->cursor);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinSetWindowPos --
+ *
+ * Adjust the stacking order of a window relative to a second
+ * window (or NULL).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Moves the specified window in the stacking order.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinSetWindowPos(hwnd, siblingHwnd, pos)
+ HWND hwnd; /* Window to restack. */
+ HWND siblingHwnd; /* Sibling window. */
+ int pos; /* One of Above or Below. */
+{
+ HWND temp;
+
+ /*
+ * Since Windows does not support Above mode, we place the
+ * specified window below the sibling and then swap them.
+ */
+
+ if (siblingHwnd) {
+ if (pos == Above) {
+ SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+ temp = hwnd;
+ hwnd = siblingHwnd;
+ siblingHwnd = temp;
+ }
+ } else {
+ siblingHwnd = (pos == Above) ? HWND_TOP : HWND_BOTTOM;
+ }
+
+ SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpWindowWasRecentlyDeleted --
+ *
+ * Determines whether we know if the window given as argument was
+ * recently deleted. Called by the generic code error handler to
+ * handle BadWindow events.
+ *
+ * Results:
+ * Always 0. We do not keep this information on Windows.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpWindowWasRecentlyDeleted(win, dispPtr)
+ Window win;
+ TkDisplay *dispPtr;
+{
+ return 0;
+}
diff --git a/win/tkWinWm.c b/win/tkWinWm.c
new file mode 100644
index 0000000..6ec1a2a
--- /dev/null
+++ b/win/tkWinWm.c
@@ -0,0 +1,4115 @@
+/*
+ * tkWinWm.c --
+ *
+ * This module takes care of the interactions between a Tk-based
+ * application and the window manager. Among other things, it
+ * implements the "wm" command and passes geometry information
+ * to the window manager.
+ *
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinWm.c 1.67 97/09/23 17:39:47
+ */
+
+#include "tkWinInt.h"
+
+/*
+ * A data structure of the following type holds information for
+ * each window manager protocol (such as WM_DELETE_WINDOW) for
+ * which a handler (i.e. a Tcl command) has been defined for a
+ * particular top-level window.
+ */
+
+typedef struct ProtocolHandler {
+ Atom protocol; /* Identifies the protocol. */
+ struct ProtocolHandler *nextPtr;
+ /* Next in list of protocol handlers for
+ * the same top-level window, or NULL for
+ * end of list. */
+ Tcl_Interp *interp; /* Interpreter in which to invoke command. */
+ char command[4]; /* Tcl command to invoke when a client
+ * message for this protocol arrives.
+ * The actual size of the structure varies
+ * to accommodate the needs of the actual
+ * command. THIS MUST BE THE LAST FIELD OF
+ * THE STRUCTURE. */
+} ProtocolHandler;
+
+#define HANDLER_SIZE(cmdLength) \
+ ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
+
+/*
+ * A data structure of the following type holds window-manager-related
+ * information for each top-level window in an application.
+ */
+
+typedef struct TkWmInfo {
+ TkWindow *winPtr; /* Pointer to main Tk information for
+ * this window. */
+ HWND wrapper; /* This is the decorative frame window
+ * created by the window manager to wrap
+ * a toplevel window. This window is
+ * a direct child of the root window. */
+ Tk_Uid titleUid; /* Title to display in window caption. If
+ * NULL, use name of widget. */
+ Tk_Uid iconName; /* Name to display in icon. */
+ TkWindow *masterPtr; /* Master window for TRANSIENT_FOR property,
+ * or NULL. */
+ XWMHints hints; /* Various pieces of information for
+ * window manager. */
+ char *leaderName; /* Path name of leader of window group
+ * (corresponds to hints.window_group).
+ * Malloc-ed. Note: this field doesn't
+ * get updated if leader is destroyed. */
+ Tk_Window icon; /* Window to use as icon for this window,
+ * or NULL. */
+ Tk_Window iconFor; /* Window for which this window is icon, or
+ * NULL if this isn't an icon for anyone. */
+
+ /*
+ * Information used to construct an XSizeHints structure for
+ * the window manager:
+ */
+
+ int defMinWidth, defMinHeight, defMaxWidth, defMaxHeight;
+ /* Default resize limits given by system. */
+ int sizeHintsFlags; /* Flags word for XSizeHints structure.
+ * If the PBaseSize flag is set then the
+ * window is gridded; otherwise it isn't
+ * gridded. */
+ int minWidth, minHeight; /* Minimum dimensions of window, in
+ * grid units, not pixels. */
+ int maxWidth, maxHeight; /* Maximum dimensions of window, in
+ * grid units, not pixels, or 0 to default. */
+ Tk_Window gridWin; /* Identifies the window that controls
+ * gridding for this top-level, or NULL if
+ * the top-level isn't currently gridded. */
+ int widthInc, heightInc; /* Increments for size changes (# pixels
+ * per step). */
+ struct {
+ int x; /* numerator */
+ int y; /* denominator */
+ } minAspect, maxAspect; /* Min/max aspect ratios for window. */
+ int reqGridWidth, reqGridHeight;
+ /* The dimensions of the window (in
+ * grid units) requested through
+ * the geometry manager. */
+ int gravity; /* Desired window gravity. */
+
+ /*
+ * Information used to manage the size and location of a window.
+ */
+
+ int width, height; /* Desired dimensions of window, specified
+ * in grid units. These values are
+ * set by the "wm geometry" command and by
+ * ConfigureNotify events (for when wm
+ * resizes window). -1 means user hasn't
+ * requested dimensions. */
+ int x, y; /* Desired X and Y coordinates for window.
+ * These values are set by "wm geometry",
+ * plus by ConfigureNotify events (when wm
+ * moves window). These numbers are
+ * different than the numbers stored in
+ * winPtr->changes because (a) they could be
+ * measured from the right or bottom edge
+ * of the screen (see WM_NEGATIVE_X and
+ * WM_NEGATIVE_Y flags) and (b) if the window
+ * has been reparented then they refer to the
+ * parent rather than the window itself. */
+ int borderWidth, borderHeight;
+ /* Width and height of window dressing, in
+ * pixels for the current style/exStyle. This
+ * includes the border on both sides of the
+ * window. */
+ int configWidth, configHeight;
+ /* Dimensions passed to last request that we
+ * issued to change geometry of window. Used
+ * to eliminate redundant resize operations. */
+ HMENU hMenu; /* the hMenu associated with this menu */
+ DWORD style, exStyle; /* Style flags for the wrapper window. */
+
+ /*
+ * List of children of the toplevel which have private colormaps.
+ */
+
+ TkWindow **cmapList; /* Array of window with private colormaps. */
+ int cmapCount; /* Number of windows in array. */
+
+ /*
+ * Miscellaneous information.
+ */
+
+ ProtocolHandler *protPtr; /* First in list of protocol handlers for
+ * this window (NULL means none). */
+ int cmdArgc; /* Number of elements in cmdArgv below. */
+ char **cmdArgv; /* Array of strings to store in the
+ * WM_COMMAND property. NULL means nothing
+ * available. */
+ char *clientMachine; /* String to store in WM_CLIENT_MACHINE
+ * property, or NULL. */
+ int flags; /* Miscellaneous flags, defined below. */
+ struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */
+} WmInfo;
+
+/*
+ * Flag values for WmInfo structures:
+ *
+ * WM_NEVER_MAPPED - non-zero means window has never been
+ * mapped; need to update all info when
+ * window is first mapped.
+ * WM_UPDATE_PENDING - non-zero means a call to UpdateGeometryInfo
+ * has already been scheduled for this
+ * window; no need to schedule another one.
+ * WM_NEGATIVE_X - non-zero means x-coordinate is measured in
+ * pixels from right edge of screen, rather
+ * than from left edge.
+ * WM_NEGATIVE_Y - non-zero means y-coordinate is measured in
+ * pixels up from bottom of screen, rather than
+ * down from top.
+ * WM_UPDATE_SIZE_HINTS - non-zero means that new size hints need to be
+ * propagated to window manager.
+ * WM_SYNC_PENDING - set to non-zero while waiting for the window
+ * manager to respond to some state change.
+ * WM_MOVE_PENDING - non-zero means the application has requested
+ * a new position for the window, but it hasn't
+ * been reflected through the window manager
+ * yet.
+ * WM_COLORAMPS_EXPLICIT - non-zero means the colormap windows were
+ * set explicitly via "wm colormapwindows".
+ * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
+ * was called the top-level itself wasn't
+ * specified, so we added it implicitly at
+ * the end of the list.
+ */
+
+#define WM_NEVER_MAPPED (1<<0)
+#define WM_UPDATE_PENDING (1<<1)
+#define WM_NEGATIVE_X (1<<2)
+#define WM_NEGATIVE_Y (1<<3)
+#define WM_UPDATE_SIZE_HINTS (1<<4)
+#define WM_SYNC_PENDING (1<<5)
+#define WM_CREATE_PENDING (1<<6)
+#define WM_MOVE_PENDING (1<<7)
+#define WM_COLORMAPS_EXPLICIT (1<<8)
+#define WM_ADDED_TOPLEVEL_COLORMAP (1<<9)
+#define WM_WIDTH_NOT_RESIZABLE (1<<10)
+#define WM_HEIGHT_NOT_RESIZABLE (1<<11)
+
+/*
+ * Window styles for various types of toplevel windows.
+ */
+
+#define WM_OVERRIDE_STYLE (WS_POPUP|WS_CLIPCHILDREN|CS_DBLCLKS)
+#define EX_OVERRIDE_STYLE (WS_EX_TOOLWINDOW)
+
+#define WM_TOPLEVEL_STYLE (WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|CS_DBLCLKS)
+#define EX_TOPLEVEL_STYLE (0)
+
+#define WM_TRANSIENT_STYLE \
+ (WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPSIBLINGS|CS_DBLCLKS)
+#define EX_TRANSIENT_STYLE (WS_EX_TOOLWINDOW | WS_EX_DLGMODALFRAME)
+
+/*
+ * This module keeps a list of all top-level windows.
+ */
+
+static WmInfo *firstWmPtr = NULL; /* Points to first top-level window. */
+static WmInfo *foregroundWmPtr = NULL; /* Points to the foreground window. */
+
+/*
+ * The variable below is used to enable or disable tracing in this
+ * module. If tracing is enabled, then information is printed on
+ * standard output about interesting interactions with the window
+ * manager.
+ */
+
+static int wmTracing = 0;
+
+/*
+ * The following structure is the official type record for geometry
+ * management of top-level windows.
+ */
+
+static void TopLevelReqProc(ClientData dummy, Tk_Window tkwin);
+
+static Tk_GeomMgr wmMgrType = {
+ "wm", /* name */
+ TopLevelReqProc, /* requestProc */
+ (Tk_GeomLostSlaveProc *) NULL, /* lostSlaveProc */
+};
+
+/*
+ * Global system palette. This value always refers to the currently
+ * installed foreground logical palette.
+ */
+
+static HPALETTE systemPalette = NULL;
+
+/*
+ * Window that is being constructed. This value is set immediately
+ * before a call to CreateWindowEx, and is used by SetLimits.
+ * This is a gross hack needed to work around Windows brain damage
+ * where it sends the WM_GETMINMAXINFO message before the WM_CREATE
+ * window.
+ */
+
+static TkWindow *createWindow = NULL;
+
+/*
+ * Flag indicating whether this module has been initialized yet.
+ */
+
+static int initialized = 0;
+
+/*
+ * Class for toplevel windows.
+ */
+
+static WNDCLASS toplevelClass;
+
+/*
+ * This flag is cleared when the first window is mapped in a non-iconic
+ * state.
+ */
+
+static int firstWindow = 1;
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static void ConfigureEvent _ANSI_ARGS_((TkWindow *winPtr,
+ XConfigureEvent *eventPtr));
+static void ConfigureTopLevel _ANSI_ARGS_((WINDOWPOS *pos));
+static void GenerateConfigureNotify _ANSI_ARGS_((
+ TkWindow *winPtr));
+static void GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
+ int *maxWidthPtr, int *maxHeightPtr));
+static void GetMinSize _ANSI_ARGS_((WmInfo *wmPtr,
+ int *minWidthPtr, int *minHeightPtr));
+static TkWindow * GetTopLevel _ANSI_ARGS_((HWND hwnd));
+static void InitWm _ANSI_ARGS_((void));
+static int InstallColormaps _ANSI_ARGS_((HWND hwnd, int message,
+ int isForemost));
+static void InvalidateSubTree _ANSI_ARGS_((TkWindow *winPtr,
+ Colormap colormap));
+static int ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
+ char *string, TkWindow *winPtr));
+static void RefreshColormap _ANSI_ARGS_((Colormap colormap));
+static void SetLimits _ANSI_ARGS_((HWND hwnd, MINMAXINFO *info));
+static LRESULT CALLBACK TopLevelProc _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+static void TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
+ Tk_Window tkwin));
+static void UpdateGeometryInfo _ANSI_ARGS_((
+ ClientData clientData));
+static void UpdateWrapper _ANSI_ARGS_((TkWindow *winPtr));
+static LRESULT CALLBACK WmProc _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitWm --
+ *
+ * This routine creates the Wm toplevel decorative frame class.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Registers a new window class.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InitWm(void)
+{
+ if (initialized) {
+ return;
+ }
+ initialized = 1;
+
+ toplevelClass.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
+ toplevelClass.cbClsExtra = 0;
+ toplevelClass.cbWndExtra = 0;
+ toplevelClass.hInstance = Tk_GetHINSTANCE();
+ toplevelClass.hbrBackground = NULL;
+ toplevelClass.lpszMenuName = NULL;
+ toplevelClass.lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME;
+ toplevelClass.lpfnWndProc = WmProc;
+ toplevelClass.hIcon = LoadIcon(Tk_GetHINSTANCE(), "tk");
+ toplevelClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+
+ if (!RegisterClass(&toplevelClass)) {
+ panic("Unable to register TkTopLevel class");
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTopLevel --
+ *
+ * This function retrieves the TkWindow associated with the
+ * given HWND.
+ *
+ * Results:
+ * Returns the matching TkWindow.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static TkWindow *
+GetTopLevel(hwnd)
+ HWND hwnd;
+{
+ /*
+ * If this function is called before the CreateWindowEx call
+ * has completed, then the user data slot will not have been
+ * set yet, so we use the global createWindow variable.
+ */
+
+ if (createWindow) {
+ return createWindow;
+ }
+ return (TkWindow *) GetWindowLong(hwnd, GWL_USERDATA);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetLimits --
+ *
+ * Updates the minimum and maximum window size constraints.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the values of the info pointer to reflect the current
+ * minimum and maximum size values.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SetLimits(hwnd, info)
+ HWND hwnd;
+ MINMAXINFO *info;
+{
+ register WmInfo *wmPtr;
+ int maxWidth, maxHeight;
+ int minWidth, minHeight;
+ int base;
+ TkWindow *winPtr = GetTopLevel(hwnd);
+
+ if (winPtr == NULL) {
+ return;
+ }
+
+ wmPtr = winPtr->wmInfoPtr;
+
+ /*
+ * Copy latest constraint info.
+ */
+
+ wmPtr->defMinWidth = info->ptMinTrackSize.x;
+ wmPtr->defMinHeight = info->ptMinTrackSize.y;
+ wmPtr->defMaxWidth = info->ptMaxTrackSize.x;
+ wmPtr->defMaxHeight = info->ptMaxTrackSize.y;
+
+ GetMaxSize(wmPtr, &maxWidth, &maxHeight);
+ GetMinSize(wmPtr, &minWidth, &minHeight);
+
+ if (wmPtr->gridWin != NULL) {
+ base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc);
+ if (base < 0) {
+ base = 0;
+ }
+ base += wmPtr->borderWidth;
+ info->ptMinTrackSize.x = base + (minWidth * wmPtr->widthInc);
+ info->ptMaxTrackSize.x = base + (maxWidth * wmPtr->widthInc);
+
+ base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc);
+ if (base < 0) {
+ base = 0;
+ }
+ base += wmPtr->borderHeight;
+ info->ptMinTrackSize.y = base + (minHeight * wmPtr->heightInc);
+ info->ptMaxTrackSize.y = base + (maxHeight * wmPtr->heightInc);
+ } else {
+ info->ptMaxTrackSize.x = maxWidth + wmPtr->borderWidth;
+ info->ptMaxTrackSize.y = maxHeight + wmPtr->borderHeight;
+ info->ptMinTrackSize.x = minWidth + wmPtr->borderWidth;
+ info->ptMinTrackSize.y = minHeight + wmPtr->borderHeight;
+ }
+
+ /*
+ * If the window isn't supposed to be resizable, then set the
+ * minimum and maximum dimensions to be the same as the current size.
+ */
+
+ if (!(wmPtr->flags & WM_SYNC_PENDING)) {
+ if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
+ info->ptMinTrackSize.x = winPtr->changes.width
+ + wmPtr->borderWidth;
+ info->ptMaxTrackSize.x = info->ptMinTrackSize.x;
+ }
+ if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
+ info->ptMinTrackSize.y = winPtr->changes.height
+ + wmPtr->borderHeight;
+ info->ptMaxTrackSize.y = info->ptMinTrackSize.y;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinWmCleanup --
+ *
+ * Unregisters classes registered by the window manager. This is
+ * called from the DLL main entry point when the DLL is unloaded.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window classes are discarded.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinWmCleanup(hInstance)
+ HINSTANCE hInstance;
+{
+ if (!initialized) {
+ return;
+ }
+ initialized = 0;
+
+ UnregisterClass(TK_WIN_TOPLEVEL_CLASS_NAME, hInstance);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmNewWindow --
+ *
+ * This procedure is invoked whenever a new top-level
+ * window is created. Its job is to initialize the WmInfo
+ * structure for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A WmInfo structure gets allocated and initialized.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmNewWindow(winPtr)
+ TkWindow *winPtr; /* Newly-created top-level window. */
+{
+ register WmInfo *wmPtr;
+
+ wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
+ winPtr->wmInfoPtr = wmPtr;
+ wmPtr->winPtr = winPtr;
+ wmPtr->wrapper = NULL;
+ wmPtr->titleUid = NULL;
+ wmPtr->iconName = NULL;
+ wmPtr->masterPtr = NULL;
+ wmPtr->hints.flags = InputHint | StateHint;
+ wmPtr->hints.input = True;
+ wmPtr->hints.initial_state = NormalState;
+ wmPtr->hints.icon_pixmap = None;
+ wmPtr->hints.icon_window = None;
+ wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
+ wmPtr->hints.icon_mask = None;
+ wmPtr->hints.window_group = None;
+ wmPtr->leaderName = NULL;
+ wmPtr->icon = NULL;
+ wmPtr->iconFor = NULL;
+ wmPtr->sizeHintsFlags = 0;
+
+ /*
+ * Default the maximum dimensions to the size of the display.
+ */
+
+ wmPtr->defMinWidth = wmPtr->defMinHeight = 0;
+ wmPtr->defMaxWidth = DisplayWidth(winPtr->display,
+ winPtr->screenNum);
+ wmPtr->defMaxHeight = DisplayHeight(winPtr->display,
+ winPtr->screenNum);
+ wmPtr->minWidth = wmPtr->minHeight = 1;
+ wmPtr->maxWidth = wmPtr->maxHeight = 0;
+ wmPtr->gridWin = NULL;
+ wmPtr->widthInc = wmPtr->heightInc = 1;
+ wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
+ wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
+ wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
+ wmPtr->gravity = NorthWestGravity;
+ wmPtr->width = -1;
+ wmPtr->height = -1;
+ wmPtr->hMenu = NULL;
+ wmPtr->x = winPtr->changes.x;
+ wmPtr->y = winPtr->changes.y;
+ wmPtr->borderWidth = 0;
+ wmPtr->borderHeight = 0;
+
+ wmPtr->cmapList = NULL;
+ wmPtr->cmapCount = 0;
+
+ wmPtr->configWidth = -1;
+ wmPtr->configHeight = -1;
+ wmPtr->protPtr = NULL;
+ wmPtr->cmdArgv = NULL;
+ wmPtr->clientMachine = NULL;
+ wmPtr->flags = WM_NEVER_MAPPED;
+ wmPtr->nextPtr = firstWmPtr;
+ firstWmPtr = wmPtr;
+
+ /*
+ * Tk must monitor structure events for top-level windows, in order
+ * to detect size and position changes caused by window managers.
+ */
+
+ Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
+ TopLevelEventProc, (ClientData) winPtr);
+
+ /*
+ * Arrange for geometry requests to be reflected from the window
+ * to the window manager.
+ */
+
+ Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateWrapper --
+ *
+ * This function creates the wrapper window that contains the
+ * window decorations and menus for a toplevel. This function
+ * may be called after a window is mapped to change the window
+ * style.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys any old wrapper window and replaces it with a newly
+ * created wrapper.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateWrapper(winPtr)
+ TkWindow *winPtr; /* Top-level window to redecorate. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ HWND parentHWND = NULL, oldWrapper;
+ HWND child = TkWinGetHWND(winPtr->window);
+ int x, y, width, height, state;
+ WINDOWPLACEMENT place;
+
+ parentHWND = NULL;
+ child = TkWinGetHWND(winPtr->window);
+
+ if (winPtr->flags & TK_EMBEDDED) {
+ wmPtr->wrapper = (HWND) winPtr->privatePtr;
+ if (wmPtr->wrapper == NULL) {
+ panic("TkWmMapWindow: Cannot find container window");
+ }
+ if (!IsWindow(wmPtr->wrapper)) {
+ panic("TkWmMapWindow: Container was destroyed");
+ }
+
+ } else {
+ /*
+ * Pick the decorative frame style. Override redirect windows get
+ * created as undecorated popups. Transient windows get a modal
+ * dialog frame. Neither override, nor transient windows appear in
+ * the Win95 taskbar. Note that a transient window does not resize
+ * by default, so we need to explicitly add the WS_THICKFRAME style
+ * if we want it to be resizeable.
+ */
+
+ if (winPtr->atts.override_redirect) {
+ wmPtr->style = WM_OVERRIDE_STYLE;
+ wmPtr->exStyle = EX_OVERRIDE_STYLE;
+ } else if (wmPtr->masterPtr) {
+ wmPtr->style = WM_TRANSIENT_STYLE;
+ wmPtr->exStyle = EX_TRANSIENT_STYLE;
+ parentHWND = Tk_GetHWND(Tk_WindowId(wmPtr->masterPtr));
+ if (! ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) &&
+ (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE))) {
+ wmPtr->style |= WS_THICKFRAME;
+ }
+ } else {
+ wmPtr->style = WM_TOPLEVEL_STYLE;
+ wmPtr->exStyle = EX_TOPLEVEL_STYLE;
+ }
+
+ /*
+ * Compute the geometry of the parent and child windows.
+ */
+
+ wmPtr->flags |= WM_CREATE_PENDING|WM_MOVE_PENDING;
+ UpdateGeometryInfo((ClientData)winPtr);
+ wmPtr->flags &= ~(WM_CREATE_PENDING|WM_MOVE_PENDING);
+
+ width = wmPtr->borderWidth + winPtr->changes.width;
+ height = wmPtr->borderHeight + winPtr->changes.height;
+
+ /*
+ * Set the initial position from the user or program specified
+ * location. If nothing has been specified, then let the system
+ * pick a location.
+ */
+
+
+ if (!(wmPtr->sizeHintsFlags & (USPosition | PPosition))
+ && (wmPtr->flags & WM_NEVER_MAPPED)) {
+ x = CW_USEDEFAULT;
+ y = CW_USEDEFAULT;
+ } else {
+ x = winPtr->changes.x;
+ y = winPtr->changes.y;
+ }
+
+ /*
+ * Create the containing window, and set the user data to point
+ * to the TkWindow.
+ */
+
+ createWindow = winPtr;
+ wmPtr->wrapper = CreateWindowEx(wmPtr->exStyle,
+ TK_WIN_TOPLEVEL_CLASS_NAME,
+ wmPtr->titleUid, wmPtr->style, x, y, width, height,
+ parentHWND, NULL, Tk_GetHINSTANCE(), NULL);
+ SetWindowLong(wmPtr->wrapper, GWL_USERDATA, (LONG) winPtr);
+ createWindow = NULL;
+
+ place.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(wmPtr->wrapper, &place);
+ wmPtr->x = place.rcNormalPosition.left;
+ wmPtr->y = place.rcNormalPosition.top;
+
+ TkInstallFrameMenu((Tk_Window) winPtr);
+ }
+
+ /*
+ * Now we need to reparent the contained window and set its
+ * style appropriately. Be sure to update the style first so that
+ * Windows doesn't try to set the focus to the child window.
+ */
+
+ SetWindowLong(child, GWL_STYLE,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+ if (winPtr->flags & TK_EMBEDDED) {
+ SetWindowLong(child, GWL_WNDPROC, (LONG) TopLevelProc);
+ }
+ oldWrapper = SetParent(child, wmPtr->wrapper);
+ if (oldWrapper && (oldWrapper != wmPtr->wrapper)
+ && (oldWrapper != GetDesktopWindow())) {
+ SetWindowLong(oldWrapper, GWL_USERDATA, (LONG) NULL);
+ DestroyWindow(oldWrapper);
+ }
+ wmPtr->flags &= ~WM_NEVER_MAPPED;
+ SendMessage(wmPtr->wrapper, TK_ATTACHWINDOW, (WPARAM) child, 0);
+
+ /*
+ * Force an initial transition from withdrawn to the real
+ * initial state.
+ */
+
+ state = wmPtr->hints.initial_state;
+ wmPtr->hints.initial_state = WithdrawnState;
+ TkpWmSetState(winPtr, state);
+
+ /*
+ * If we are embedded then force a mapping of the window now,
+ * because we do not necessarily own the wrapper and may not
+ * get another opportunity to map ourselves. We should not be
+ * in either iconified or zoomed states when we get here, so
+ * it is safe to just check for TK_EMBEDDED without checking
+ * what state we are supposed to be in (default to NormalState).
+ */
+
+ if (winPtr->flags & TK_EMBEDDED) {
+ XMapWindow(winPtr->display, winPtr->window);
+ }
+
+ /*
+ * Set up menus on the wrapper if required.
+ */
+
+ if (wmPtr->hMenu != NULL) {
+ wmPtr->flags = WM_SYNC_PENDING;
+ SetMenu(wmPtr->wrapper, wmPtr->hMenu);
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+ }
+
+ /*
+ * If this is the first window created by the application, then
+ * we should activate the initial window.
+ */
+
+ if (firstWindow) {
+ firstWindow = 0;
+ SetActiveWindow(wmPtr->wrapper);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmMapWindow --
+ *
+ * This procedure is invoked to map a top-level window. This
+ * module gets a chance to update all window-manager-related
+ * information in properties before the window manager sees
+ * the map event and checks the properties. It also gets to
+ * decide whether or not to even map the window after all.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Properties of winPtr may get updated to provide up-to-date
+ * information to the window manager. The window may also get
+ * mapped, but it may not be if this procedure decides that
+ * isn't appropriate (e.g. because the window is withdrawn).
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmMapWindow(winPtr)
+ TkWindow *winPtr; /* Top-level window that's about to
+ * be mapped. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ if (!initialized) {
+ InitWm();
+ }
+
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ if (wmPtr->hints.initial_state == WithdrawnState) {
+ return;
+ }
+
+ /*
+ * Map the window in either the iconified or normal state. Note that
+ * we only send a map event if the window is in the normal state.
+ */
+
+ TkpWmSetState(winPtr, wmPtr->hints.initial_state);
+ }
+
+ /*
+ * This is the first time this window has ever been mapped.
+ * Store all the window-manager-related information for the
+ * window.
+ */
+
+ if (wmPtr->titleUid == NULL) {
+ wmPtr->titleUid = winPtr->nameUid;
+ }
+ UpdateWrapper(winPtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmUnmapWindow --
+ *
+ * This procedure is invoked to unmap a top-level window. The
+ * only thing it does special is unmap the decorative frame before
+ * unmapping the toplevel window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Unmaps the decorative frame and the window.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmUnmapWindow(winPtr)
+ TkWindow *winPtr; /* Top-level window that's about to
+ * be unmapped. */
+{
+ TkpWmSetState(winPtr, WithdrawnState);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpWmSetState --
+ *
+ * Sets the window manager state for the wrapper window of a
+ * given toplevel window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May maximize, minimize, restore, or withdraw a window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpWmSetState(winPtr, state)
+ TkWindow *winPtr; /* Toplevel window to operate on. */
+ int state; /* One of IconicState, ZoomState, NormalState,
+ * or WithdrawnState. */
+{
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+ int cmd;
+
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ wmPtr->hints.initial_state = state;
+ return;
+ }
+
+ wmPtr->flags |= WM_SYNC_PENDING;
+ if (state == WithdrawnState) {
+ cmd = SW_HIDE;
+ } else if (state == IconicState) {
+ cmd = SW_SHOWMINNOACTIVE;
+ } else if (state == NormalState) {
+ cmd = SW_SHOWNOACTIVATE;
+ } else if (state == ZoomState) {
+ cmd = SW_SHOWMAXIMIZED;
+ }
+ ShowWindow(wmPtr->wrapper, cmd);
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmDeadWindow --
+ *
+ * This procedure is invoked when a top-level window is
+ * about to be deleted. It cleans up the wm-related data
+ * structures for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The WmInfo structure for winPtr gets freed up.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmDeadWindow(winPtr)
+ TkWindow *winPtr; /* Top-level window that's being deleted. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ WmInfo *wmPtr2;
+
+ if (wmPtr == NULL) {
+ return;
+ }
+
+ /*
+ * Clean up event related window info.
+ */
+
+ if (firstWmPtr == wmPtr) {
+ firstWmPtr = wmPtr->nextPtr;
+ } else {
+ register WmInfo *prevPtr;
+ for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ panic("couldn't unlink window in TkWmDeadWindow");
+ }
+ if (prevPtr->nextPtr == wmPtr) {
+ prevPtr->nextPtr = wmPtr->nextPtr;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Reset all transient windows whose master is the dead window.
+ */
+
+ for (wmPtr2 = firstWmPtr; wmPtr2 != NULL; wmPtr2 = wmPtr2->nextPtr) {
+ if (wmPtr2->masterPtr == winPtr) {
+ wmPtr2->masterPtr = NULL;
+ if ((wmPtr2->wrapper != None)
+ && !(wmPtr2->flags & (WM_NEVER_MAPPED))) {
+ UpdateWrapper(wmPtr2->winPtr);
+ }
+ }
+ }
+
+ if (wmPtr->hints.flags & IconPixmapHint) {
+ Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
+ }
+ if (wmPtr->hints.flags & IconMaskHint) {
+ Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
+ }
+ if (wmPtr->leaderName != NULL) {
+ ckfree(wmPtr->leaderName);
+ }
+ if (wmPtr->icon != NULL) {
+ wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr2->iconFor = NULL;
+ }
+ if (wmPtr->iconFor != NULL) {
+ wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
+ wmPtr2->icon = NULL;
+ wmPtr2->hints.flags &= ~IconWindowHint;
+ }
+ while (wmPtr->protPtr != NULL) {
+ ProtocolHandler *protPtr;
+
+ protPtr = wmPtr->protPtr;
+ wmPtr->protPtr = protPtr->nextPtr;
+ Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
+ }
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ }
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ }
+ if (wmPtr->flags & WM_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
+ }
+
+ /*
+ * Destroy the decorative frame window.
+ */
+
+ if (!(winPtr->flags & TK_EMBEDDED)) {
+ if (wmPtr->wrapper != NULL) {
+ DestroyWindow(wmPtr->wrapper);
+ } else {
+ DestroyWindow(Tk_GetHWND(winPtr->window));
+ }
+ }
+ ckfree((char *) wmPtr);
+ winPtr->wmInfoPtr = NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmSetClass --
+ *
+ * This procedure is invoked whenever a top-level window's
+ * class is changed. If the window has been mapped then this
+ * procedure updates the window manager property for the
+ * class. If the window hasn't been mapped, the update is
+ * deferred until just before the first mapping.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A window property may get updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmSetClass(winPtr)
+ TkWindow *winPtr; /* Newly-created top-level window. */
+{
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_WmCmd --
+ *
+ * This procedure is invoked to process the "wm" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+int
+Tk_WmCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window) clientData;
+ TkWindow *winPtr;
+ register WmInfo *wmPtr;
+ int c;
+ size_t length;
+
+ if (argc < 2) {
+ wrongNumArgs:
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option window ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
+ && (length >= 3)) {
+ if ((argc != 2) && (argc != 3)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " tracing ?boolean?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 2) {
+ interp->result = (wmTracing) ? "on" : "off";
+ return TCL_OK;
+ }
+ return Tcl_GetBoolean(interp, argv[2], &wmTracing);
+ }
+
+ if (argc < 3) {
+ goto wrongNumArgs;
+ }
+ winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ Tcl_AppendResult(interp, "window \"", winPtr->pathName,
+ "\" isn't a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr = winPtr->wmInfoPtr;
+ if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
+ int numer1, denom1, numer2, denom2;
+
+ if ((argc != 3) && (argc != 7)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " aspect window ?minNumer minDenom ",
+ "maxNumer maxDenom?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & PAspect) {
+ sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
+ wmPtr->minAspect.y, wmPtr->maxAspect.x,
+ wmPtr->maxAspect.y);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~PAspect;
+ } else {
+ if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
+ (denom2 <= 0)) {
+ interp->result = "aspect number can't be <= 0";
+ return TCL_ERROR;
+ }
+ wmPtr->minAspect.x = numer1;
+ wmPtr->minAspect.y = denom1;
+ wmPtr->maxAspect.x = numer2;
+ wmPtr->maxAspect.y = denom2;
+ wmPtr->sizeHintsFlags |= PAspect;
+ }
+ goto updateGeom;
+ } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " client window ?name?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->clientMachine != NULL) {
+ interp->result = wmPtr->clientMachine;
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == 0) {
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ wmPtr->clientMachine = NULL;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XDeleteProperty(winPtr->display, winPtr->window,
+ Tk_InternAtom((Tk_Window) winPtr,
+ "WM_CLIENT_MACHINE"));
+ }
+ }
+ return TCL_OK;
+ }
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ }
+ wmPtr->clientMachine = (char *)
+ ckalloc((unsigned) (strlen(argv[3]) + 1));
+ strcpy(wmPtr->clientMachine, argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XTextProperty textProp;
+ if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
+ != 0) {
+ XSetWMClientMachine(winPtr->display, winPtr->window,
+ &textProp);
+ XFree((char *) textProp.value);
+ }
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
+ && (length >= 3)) {
+ TkWindow **cmapList;
+ TkWindow *winPtr2;
+ int i, windowArgc, gotToplevel;
+ char **windowArgv;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " colormapwindows window ?windowList?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ Tk_MakeWindowExist((Tk_Window) winPtr);
+ for (i = 0; i < wmPtr->cmapCount; i++) {
+ if ((i == (wmPtr->cmapCount-1))
+ && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
+ break;
+ }
+ Tcl_AppendElement(interp, wmPtr->cmapList[i]->pathName);
+ }
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ cmapList = (TkWindow **) ckalloc((unsigned)
+ ((windowArgc+1)*sizeof(TkWindow*)));
+ for (i = 0; i < windowArgc; i++) {
+ winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
+ tkwin);
+ if (winPtr2 == NULL) {
+ ckfree((char *) cmapList);
+ ckfree((char *) windowArgv);
+ return TCL_ERROR;
+ }
+ if (winPtr2 == winPtr) {
+ gotToplevel = 1;
+ }
+ if (winPtr2->window == None) {
+ Tk_MakeWindowExist((Tk_Window) winPtr2);
+ }
+ cmapList[i] = winPtr2;
+ }
+ if (!gotToplevel) {
+ wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
+ cmapList[windowArgc] = winPtr;
+ windowArgc++;
+ } else {
+ wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
+ }
+ wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
+ if (wmPtr->cmapList != NULL) {
+ ckfree((char *)wmPtr->cmapList);
+ }
+ wmPtr->cmapList = cmapList;
+ wmPtr->cmapCount = windowArgc;
+ ckfree((char *) windowArgv);
+
+ /*
+ * Now we need to force the updated colormaps to be installed.
+ */
+
+ if (wmPtr == foregroundWmPtr) {
+ InstallColormaps(wmPtr->wrapper, WM_QUERYNEWPALETTE, 1);
+ } else {
+ InstallColormaps(wmPtr->wrapper, WM_PALETTECHANGED, 0);
+ }
+ return TCL_OK;
+ } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
+ && (length >= 3)) {
+ int cmdArgc;
+ char **cmdArgv;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " command window ?value?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->cmdArgv != NULL) {
+ interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
+ interp->freeProc = TCL_DYNAMIC;
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == 0) {
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ wmPtr->cmdArgv = NULL;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XDeleteProperty(winPtr->display, winPtr->window,
+ Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
+ }
+ }
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ }
+ wmPtr->cmdArgc = cmdArgc;
+ wmPtr->cmdArgv = cmdArgv;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " deiconify window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't deiconify ", argv[2],
+ ": it is an icon for ", winPtr->pathName, (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (winPtr->flags & TK_EMBEDDED) {
+ Tcl_AppendResult(interp, "can't deiconify ", winPtr->pathName,
+ ": it is an embedded window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ TkpWmSetState(winPtr, NormalState);
+ } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " focusmodel window ?active|passive?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = wmPtr->hints.input ? "passive" : "active";
+ return TCL_OK;
+ }
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
+ wmPtr->hints.input = False;
+ } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
+ wmPtr->hints.input = True;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be active or passive", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
+ && (length >= 2)) {
+ HWND hwnd;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " frame window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ hwnd = wmPtr->wrapper;
+ if (hwnd == NULL) {
+ hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) winPtr));
+ }
+ sprintf(interp->result, "0x%x", (unsigned int) hwnd);
+ } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
+ && (length >= 2)) {
+ char xSign, ySign;
+ int width, height;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " geometry window ?newGeometry?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
+ ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
+ if (wmPtr->gridWin != NULL) {
+ width = wmPtr->reqGridWidth + (winPtr->changes.width
+ - winPtr->reqWidth)/wmPtr->widthInc;
+ height = wmPtr->reqGridHeight + (winPtr->changes.height
+ - winPtr->reqHeight)/wmPtr->heightInc;
+ } else {
+ width = winPtr->changes.width;
+ height = winPtr->changes.height;
+ }
+ sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
+ xSign, wmPtr->x, ySign, wmPtr->y);
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->width = -1;
+ wmPtr->height = -1;
+ goto updateGeom;
+ }
+ return ParseGeometry(interp, argv[3], winPtr);
+ } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
+ && (length >= 3)) {
+ int reqWidth, reqHeight, widthInc, heightInc;
+
+ if ((argc != 3) && (argc != 7)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " grid window ?baseWidth baseHeight ",
+ "widthInc heightInc?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & PBaseSize) {
+ sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
+ wmPtr->reqGridHeight, wmPtr->widthInc,
+ wmPtr->heightInc);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ /*
+ * Turn off gridding and reset the width and height
+ * to make sense as ungridded numbers.
+ */
+
+ wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
+ if (wmPtr->width != -1) {
+ wmPtr->width = winPtr->reqWidth + (wmPtr->width
+ - wmPtr->reqGridWidth)*wmPtr->widthInc;
+ wmPtr->height = winPtr->reqHeight + (wmPtr->height
+ - wmPtr->reqGridHeight)*wmPtr->heightInc;
+ }
+ wmPtr->widthInc = 1;
+ wmPtr->heightInc = 1;
+ } else {
+ if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (reqWidth < 0) {
+ interp->result = "baseWidth can't be < 0";
+ return TCL_ERROR;
+ }
+ if (reqHeight < 0) {
+ interp->result = "baseHeight can't be < 0";
+ return TCL_ERROR;
+ }
+ if (widthInc < 0) {
+ interp->result = "widthInc can't be < 0";
+ return TCL_ERROR;
+ }
+ if (heightInc < 0) {
+ interp->result = "heightInc can't be < 0";
+ return TCL_ERROR;
+ }
+ Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
+ heightInc);
+ }
+ goto updateGeom;
+ } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
+ && (length >= 3)) {
+ Tk_Window tkwin2;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " group window ?pathName?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & WindowGroupHint) {
+ interp->result = wmPtr->leaderName;
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~WindowGroupHint;
+ if (wmPtr->leaderName != NULL) {
+ ckfree(wmPtr->leaderName);
+ }
+ wmPtr->leaderName = NULL;
+ } else {
+ tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ Tk_MakeWindowExist(tkwin2);
+ wmPtr->hints.window_group = Tk_WindowId(tkwin2);
+ wmPtr->hints.flags |= WindowGroupHint;
+ wmPtr->leaderName = ckalloc((unsigned) (strlen(argv[3])+1));
+ strcpy(wmPtr->leaderName, argv[3]);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
+ && (length >= 5)) {
+ Pixmap pixmap;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconbitmap window ?bitmap?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconPixmapHint) {
+ interp->result = Tk_NameOfBitmap(winPtr->display,
+ wmPtr->hints.icon_pixmap);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ if (wmPtr->hints.icon_pixmap != None) {
+ Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
+ }
+ wmPtr->hints.flags &= ~IconPixmapHint;
+ } else {
+ pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
+ Tk_GetUid(argv[3]));
+ if (pixmap == None) {
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_pixmap = pixmap;
+ wmPtr->hints.flags |= IconPixmapHint;
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
+ && (length >= 5)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconify window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
+ Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
+ "\": override-redirect flag is set", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->masterPtr != NULL) {
+ Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
+ "\": it is a transient", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't iconify ", argv[2],
+ ": it is an icon for ", winPtr->pathName, (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (winPtr->flags & TK_EMBEDDED) {
+ Tcl_AppendResult(interp, "can't iconify ", winPtr->pathName,
+ ": it is an embedded window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ TkpWmSetState(winPtr, IconicState);
+ } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
+ && (length >= 5)) {
+ Pixmap pixmap;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconmask window ?bitmap?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconMaskHint) {
+ interp->result = Tk_NameOfBitmap(winPtr->display,
+ wmPtr->hints.icon_mask);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ if (wmPtr->hints.icon_mask != None) {
+ Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
+ }
+ wmPtr->hints.flags &= ~IconMaskHint;
+ } else {
+ pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
+ if (pixmap == None) {
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_mask = pixmap;
+ wmPtr->hints.flags |= IconMaskHint;
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
+ && (length >= 5)) {
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconname window ?newName?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
+ return TCL_OK;
+ } else {
+ wmPtr->iconName = Tk_GetUid(argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
+ && (length >= 5)) {
+ int x, y;
+
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconposition window ?x y?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconPositionHint) {
+ sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
+ wmPtr->hints.icon_y);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~IconPositionHint;
+ } else {
+ if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_x = x;
+ wmPtr->hints.icon_y = y;
+ wmPtr->hints.flags |= IconPositionHint;
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
+ && (length >= 5)) {
+ Tk_Window tkwin2;
+ WmInfo *wmPtr2;
+ XSetWindowAttributes atts;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconwindow window ?pathName?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->icon != NULL) {
+ interp->result = Tk_PathName(wmPtr->icon);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~IconWindowHint;
+ if (wmPtr->icon != NULL) {
+ /*
+ * Let the window use button events again, then remove
+ * it as icon window.
+ */
+
+ atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
+ | ButtonPressMask;
+ Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
+ wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr2->iconFor = NULL;
+ wmPtr2->hints.initial_state = WithdrawnState;
+ }
+ wmPtr->icon = NULL;
+ } else {
+ tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ if (!Tk_IsTopLevel(tkwin2)) {
+ Tcl_AppendResult(interp, "can't use ", argv[3],
+ " as icon window: not at top level", (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
+ if (wmPtr2->iconFor != NULL) {
+ Tcl_AppendResult(interp, argv[3], " is already an icon for ",
+ Tk_PathName(wmPtr2->iconFor), (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->icon != NULL) {
+ WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr3->iconFor = NULL;
+
+ /*
+ * Let the window use button events again.
+ */
+
+ atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
+ | ButtonPressMask;
+ Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
+ }
+
+ /*
+ * Disable button events in the icon window: some window
+ * managers (like olvwm) want to get the events themselves,
+ * but X only allows one application at a time to receive
+ * button events for a window.
+ */
+
+ atts.event_mask = Tk_Attributes(tkwin2)->event_mask
+ & ~ButtonPressMask;
+ Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
+ Tk_MakeWindowExist(tkwin2);
+ wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
+ wmPtr->hints.flags |= IconWindowHint;
+ wmPtr->icon = tkwin2;
+ wmPtr2->iconFor = (Tk_Window) winPtr;
+ if (!(wmPtr2->flags & WM_NEVER_MAPPED)) {
+ if (XWithdrawWindow(Tk_Display(tkwin2), Tk_WindowId(tkwin2),
+ Tk_ScreenNumber(tkwin2)) == 0) {
+ interp->result =
+ "couldn't send withdraw message to window manager";
+ return TCL_ERROR;
+ }
+ }
+ }
+ } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
+ && (length >= 2)) {
+ int width, height;
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " maxsize window ?width height?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ GetMaxSize(wmPtr, &width, &height);
+ sprintf(interp->result, "%d %d", width, height);
+ return TCL_OK;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ wmPtr->maxWidth = width;
+ wmPtr->maxHeight = height;
+ goto updateGeom;
+ } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
+ && (length >= 2)) {
+ int width, height;
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " minsize window ?width height?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ GetMinSize(wmPtr, &width, &height);
+ sprintf(interp->result, "%d %d", width, height);
+ return TCL_OK;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ wmPtr->minWidth = width;
+ wmPtr->minHeight = height;
+ goto updateGeom;
+ } else if ((c == 'o')
+ && (strncmp(argv[1], "overrideredirect", length) == 0)) {
+ int boolean;
+ XSetWindowAttributes atts;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " overrideredirect window ?boolean?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
+ interp->result = "1";
+ } else {
+ interp->result = "0";
+ }
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ atts.override_redirect = (boolean) ? True : False;
+ Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
+ &atts);
+ if (!(wmPtr->flags & (WM_NEVER_MAPPED)
+ && !(winPtr->flags & TK_EMBEDDED))) {
+ UpdateWrapper(winPtr);
+ }
+ } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " positionfrom window ?user/program?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & USPosition) {
+ interp->result = "user";
+ } else if (wmPtr->sizeHintsFlags & PPosition) {
+ interp->result = "program";
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
+ } else {
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~PPosition;
+ wmPtr->sizeHintsFlags |= USPosition;
+ } else if ((c == 'p')
+ && (strncmp(argv[3], "program", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~USPosition;
+ wmPtr->sizeHintsFlags |= PPosition;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be program or user", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ goto updateGeom;
+ } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
+ && (length >= 2)) {
+ register ProtocolHandler *protPtr, *prevPtr;
+ Atom protocol;
+ int cmdLength;
+
+ if ((argc < 3) || (argc > 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " protocol window ?name? ?command?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ /*
+ * Return a list of all defined protocols for the window.
+ */
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ Tcl_AppendElement(interp,
+ Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
+ }
+ return TCL_OK;
+ }
+ protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
+ if (argc == 4) {
+ /*
+ * Return the command to handle a given protocol.
+ */
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ if (protPtr->protocol == protocol) {
+ interp->result = protPtr->command;
+ return TCL_OK;
+ }
+ }
+ return TCL_OK;
+ }
+
+ /*
+ * Delete any current protocol handler, then create a new
+ * one with the specified command, unless the command is
+ * empty.
+ */
+
+ for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
+ prevPtr = protPtr, protPtr = protPtr->nextPtr) {
+ if (protPtr->protocol == protocol) {
+ if (prevPtr == NULL) {
+ wmPtr->protPtr = protPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = protPtr->nextPtr;
+ }
+ Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
+ break;
+ }
+ }
+ cmdLength = strlen(argv[4]);
+ if (cmdLength > 0) {
+ protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
+ protPtr->protocol = protocol;
+ protPtr->nextPtr = wmPtr->protPtr;
+ wmPtr->protPtr = protPtr;
+ protPtr->interp = interp;
+ strcpy(protPtr->command, argv[4]);
+ }
+ } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
+ int width, height;
+
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " resizable window ?width height?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ sprintf(interp->result, "%d %d",
+ (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
+ (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
+ return TCL_OK;
+ }
+ if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (width) {
+ wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
+ } else {
+ wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
+ }
+ if (height) {
+ wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
+ } else {
+ wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " sizefrom window ?user|program?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & USSize) {
+ interp->result = "user";
+ } else if (wmPtr->sizeHintsFlags & PSize) {
+ interp->result = "program";
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~(USSize|PSize);
+ } else {
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~PSize;
+ wmPtr->sizeHintsFlags |= USSize;
+ } else if ((c == 'p')
+ && (strncmp(argv[3], "program", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~USSize;
+ wmPtr->sizeHintsFlags |= PSize;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be program or user", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ goto updateGeom;
+ } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " state window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ interp->result = "icon";
+ } else {
+ switch (wmPtr->hints.initial_state) {
+ case NormalState:
+ interp->result = "normal";
+ break;
+ case IconicState:
+ interp->result = "iconic";
+ break;
+ case WithdrawnState:
+ interp->result = "withdrawn";
+ break;
+ case ZoomState:
+ interp->result = "zoomed";
+ break;
+ }
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
+ && (length >= 2)) {
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " title window ?newTitle?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
+ : winPtr->nameUid;
+ return TCL_OK;
+ } else {
+ wmPtr->titleUid = Tk_GetUid(argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED) && wmPtr->wrapper != NULL) {
+ SetWindowText(wmPtr->wrapper, wmPtr->titleUid);
+ }
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
+ && (length >= 3)) {
+ TkWindow *masterPtr;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " transient window ?master?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->masterPtr != NULL) {
+ Tcl_SetResult(interp, Tk_PathName(wmPtr->masterPtr),
+ TCL_STATIC);
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == '\0') {
+ wmPtr->masterPtr = NULL;
+ } else {
+ masterPtr = (TkWindow*) Tk_NameToWindow(interp, argv[3], tkwin);
+ if (masterPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (masterPtr == winPtr) {
+ wmPtr->masterPtr = NULL;
+ } else {
+ Tk_MakeWindowExist((Tk_Window)masterPtr);
+
+ /*
+ * Ensure that the master window is actually a Tk toplevel.
+ */
+
+ while (!(masterPtr->flags & TK_TOP_LEVEL)) {
+ masterPtr = masterPtr->parentPtr;
+ }
+ wmPtr->masterPtr = masterPtr;
+
+ /*
+ * Ensure that the transient window is either mapped or
+ * unmapped like its master.
+ */
+
+ TkpWmSetState(winPtr, NormalState);
+ }
+ }
+ if (!(wmPtr->flags & (WM_NEVER_MAPPED)
+ && !(winPtr->flags & TK_EMBEDDED))) {
+ UpdateWrapper(winPtr);
+ }
+ } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " withdraw window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't withdraw ", argv[2],
+ ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ TkpWmSetState(winPtr, WithdrawnState);
+ } else {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
+ "\": must be aspect, client, command, deiconify, ",
+ "focusmodel, frame, geometry, grid, group, iconbitmap, ",
+ "iconify, iconmask, iconname, iconposition, ",
+ "iconwindow, maxsize, minsize, overrideredirect, ",
+ "positionfrom, protocol, resizable, sizefrom, state, title, ",
+ "transient, or withdraw",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+
+ updateGeom:
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_SetGrid --
+ *
+ * This procedure is invoked by a widget when it wishes to set a grid
+ * coordinate system that controls the size of a top-level window.
+ * It provides a C interface equivalent to the "wm grid" command and
+ * is usually asscoiated with the -setgrid option.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Grid-related information will be passed to the window manager, so
+ * that the top-level window associated with tkwin will resize on
+ * even grid units. If some other window already controls gridding
+ * for the top-level window then this procedure call has no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
+ Tk_Window tkwin; /* Token for window. New window mgr info
+ * will be posted for the top-level window
+ * associated with this window. */
+ int reqWidth; /* Width (in grid units) corresponding to
+ * the requested geometry for tkwin. */
+ int reqHeight; /* Height (in grid units) corresponding to
+ * the requested geometry for tkwin. */
+ int widthInc, heightInc; /* Pixel increments corresponding to a
+ * change of one grid unit. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ register WmInfo *wmPtr;
+
+ /*
+ * Find the top-level window for tkwin, plus the window manager
+ * information.
+ */
+
+ while (!(winPtr->flags & TK_TOP_LEVEL)) {
+ winPtr = winPtr->parentPtr;
+ }
+ wmPtr = winPtr->wmInfoPtr;
+
+ if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
+ return;
+ }
+
+ if ((wmPtr->reqGridWidth == reqWidth)
+ && (wmPtr->reqGridHeight == reqHeight)
+ && (wmPtr->widthInc == widthInc)
+ && (wmPtr->heightInc == heightInc)
+ && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
+ == PBaseSize|PResizeInc)) {
+ return;
+ }
+
+ /*
+ * If gridding was previously off, then forget about any window
+ * size requests made by the user or via "wm geometry": these are
+ * in pixel units and there's no easy way to translate them to
+ * grid units since the new requested size of the top-level window in
+ * pixels may not yet have been registered yet (it may filter up
+ * the hierarchy in DoWhenIdle handlers). However, if the window
+ * has never been mapped yet then just leave the window size alone:
+ * assume that it is intended to be in grid units but just happened
+ * to have been specified before this procedure was called.
+ */
+
+ if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
+ wmPtr->width = -1;
+ wmPtr->height = -1;
+ }
+
+ /*
+ * Set the new gridding information, and start the process of passing
+ * all of this information to the window manager.
+ */
+
+ wmPtr->gridWin = tkwin;
+ wmPtr->reqGridWidth = reqWidth;
+ wmPtr->reqGridHeight = reqHeight;
+ wmPtr->widthInc = widthInc;
+ wmPtr->heightInc = heightInc;
+ wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_UnsetGrid --
+ *
+ * This procedure cancels the effect of a previous call
+ * to Tk_SetGrid.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If tkwin currently controls gridding for its top-level window,
+ * gridding is cancelled for that top-level window; if some other
+ * window controls gridding then this procedure has no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_UnsetGrid(tkwin)
+ Tk_Window tkwin; /* Token for window that is currently
+ * controlling gridding. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ register WmInfo *wmPtr;
+
+ /*
+ * Find the top-level window for tkwin, plus the window manager
+ * information.
+ */
+
+ while (!(winPtr->flags & TK_TOP_LEVEL)) {
+ winPtr = winPtr->parentPtr;
+ }
+ wmPtr = winPtr->wmInfoPtr;
+ if (tkwin != wmPtr->gridWin) {
+ return;
+ }
+
+ wmPtr->gridWin = NULL;
+ wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
+ if (wmPtr->width != -1) {
+ wmPtr->width = winPtr->reqWidth + (wmPtr->width
+ - wmPtr->reqGridWidth)*wmPtr->widthInc;
+ wmPtr->height = winPtr->reqHeight + (wmPtr->height
+ - wmPtr->reqGridHeight)*wmPtr->heightInc;
+ }
+ wmPtr->widthInc = 1;
+ wmPtr->heightInc = 1;
+
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TopLevelEventProc --
+ *
+ * This procedure is invoked when a top-level (or other externally-
+ * managed window) is restructured in any way.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tk's internal data structures for the window get modified to
+ * reflect the structural change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+TopLevelEventProc(clientData, eventPtr)
+ ClientData clientData; /* Window for which event occurred. */
+ XEvent *eventPtr; /* Event that just happened. */
+{
+ register TkWindow *winPtr = (TkWindow *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ Tk_ErrorHandler handler;
+
+ if (!(winPtr->flags & TK_ALREADY_DEAD)) {
+ /*
+ * A top-level window was deleted externally (e.g., by the window
+ * manager). This is probably not a good thing, but cleanup as
+ * best we can. The error handler is needed because
+ * Tk_DestroyWindow will try to destroy the window, but of course
+ * it's already gone.
+ */
+
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ Tk_DestroyWindow((Tk_Window) winPtr);
+ Tk_DeleteErrorHandler(handler);
+ }
+ }
+ else if (eventPtr->type == ConfigureNotify) {
+ WmInfo *wmPtr;
+ wmPtr = winPtr->wmInfoPtr;
+
+ if (winPtr->flags & TK_EMBEDDED) {
+ Tk_Window tkwin = (Tk_Window)winPtr;
+ SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
+ Tk_ReqHeight(tkwin));
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TopLevelReqProc --
+ *
+ * This procedure is invoked by the geometry manager whenever
+ * the requested size for a top-level window is changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Arrange for the window to be resized to satisfy the request
+ * (this happens as a when-idle action).
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+TopLevelReqProc(dummy, tkwin)
+ ClientData dummy; /* Not used. */
+ Tk_Window tkwin; /* Information about window. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ WmInfo *wmPtr;
+
+ wmPtr = winPtr->wmInfoPtr;
+ if (winPtr->flags & TK_EMBEDDED) {
+ SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin),
+ Tk_ReqHeight(tkwin));
+ }
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateGeometryInfo --
+ *
+ * This procedure is invoked when a top-level window is first
+ * mapped, and also as a when-idle procedure, to bring the
+ * geometry and/or position of a top-level window back into
+ * line with what has been requested by the user and/or widgets.
+ * This procedure doesn't return until the system has
+ * responded to the geometry change.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window's size and location may change, unless the WM prevents
+ * that from happening.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateGeometryInfo(clientData)
+ ClientData clientData; /* Pointer to the window's record. */
+{
+ int x, y; /* Position of border on desktop. */
+ int width, height; /* Size of client area. */
+ RECT rect;
+ register TkWindow *winPtr = (TkWindow *) clientData;
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ wmPtr->flags &= ~WM_UPDATE_PENDING;
+
+ /*
+ * If the window is minimized or maximized, we should not update
+ * our geometry since it will end up with the wrong values.
+ * ConfigureToplevel will reschedule UpdateGeometryInfo when the
+ * state of the window changes.
+ */
+
+ if (IsIconic(wmPtr->wrapper) || IsZoomed(wmPtr->wrapper)) {
+ return;
+ }
+
+ /*
+ * Compute the border size for the current window style. This
+ * size will include the resize handles, the title bar and the
+ * menubar. Note that this size will not be correct if the
+ * menubar spans multiple lines. The height will be off by a
+ * multiple of the menubar height. It really only measures the
+ * minimum size of the border.
+ */
+
+ rect.left = rect.right = rect.top = rect.bottom = 0;
+ AdjustWindowRectEx(&rect, wmPtr->style, wmPtr->hMenu != NULL,
+ wmPtr->exStyle);
+ wmPtr->borderWidth = rect.right - rect.left;
+ wmPtr->borderHeight = rect.bottom - rect.top;
+
+ /*
+ * Compute the new size for the top-level window. See the
+ * user documentation for details on this, but the size
+ * requested depends on (a) the size requested internally
+ * by the window's widgets, (b) the size requested by the
+ * user in a "wm geometry" command or via wm-based interactive
+ * resizing (if any), and (c) whether or not the window is
+ * gridded. Don't permit sizes <= 0 because this upsets
+ * the X server.
+ */
+
+ if (wmPtr->width == -1) {
+ width = winPtr->reqWidth;
+ } else if (wmPtr->gridWin != NULL) {
+ width = winPtr->reqWidth
+ + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
+ } else {
+ width = wmPtr->width;
+ }
+ if (width <= 0) {
+ width = 1;
+ }
+ if (wmPtr->height == -1) {
+ height = winPtr->reqHeight;
+ } else if (wmPtr->gridWin != NULL) {
+ height = winPtr->reqHeight
+ + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
+ } else {
+ height = wmPtr->height;
+ }
+ if (height <= 0) {
+ height = 1;
+ }
+
+ /*
+ * Compute the new position for the upper-left pixel of the window's
+ * decorative frame. This is tricky, because we need to include the
+ * border widths supplied by a reparented parent in this calculation,
+ * but can't use the parent's current overall size since that may
+ * change as a result of this code.
+ */
+
+ if (wmPtr->flags & WM_NEGATIVE_X) {
+ x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x
+ - (width + wmPtr->borderWidth);
+ } else {
+ x = wmPtr->x;
+ }
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y
+ - (height + wmPtr->borderHeight);
+ } else {
+ y = wmPtr->y;
+ }
+
+ /*
+ * If this window is embedded and the container is also in this
+ * process, we don't need to do anything special about the
+ * geometry, except to make sure that the desired size is known
+ * by the container. Also, zero out any position information,
+ * since embedded windows are not allowed to move.
+ */
+
+ if (winPtr->flags & TK_BOTH_HALVES) {
+ wmPtr->x = wmPtr->y = 0;
+ wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
+ Tk_GeometryRequest((Tk_Window) TkpGetOtherWindow(winPtr),
+ width, height);
+ return;
+ }
+
+ /*
+ * Reconfigure the window if it isn't already configured correctly. Base
+ * the size check on what we *asked for* last time, not what we got.
+ * Return immediately if there have been no changes in the requested
+ * geometry of the toplevel.
+ */
+ /* TODO: need to add flag for possible menu size change */
+
+ if (!((wmPtr->flags & WM_MOVE_PENDING)
+ || (width != wmPtr->configWidth)
+ || (height != wmPtr->configHeight))) {
+ return;
+ }
+ wmPtr->flags &= ~WM_MOVE_PENDING;
+
+ wmPtr->configWidth = width;
+ wmPtr->configHeight = height;
+
+ /*
+ * Don't bother moving the window if we are in the process of
+ * creating it. Just update the geometry info based on what
+ * we asked for.
+ */
+
+ if (wmPtr->flags & WM_CREATE_PENDING) {
+ winPtr->changes.x = x;
+ winPtr->changes.y = y;
+ winPtr->changes.width = width;
+ winPtr->changes.height = height;
+ return;
+ }
+
+ wmPtr->flags |= WM_SYNC_PENDING;
+ if (winPtr->flags & TK_EMBEDDED) {
+ /*
+ * The wrapper window is in a different process, so we need
+ * to send it a geometry request. This protocol assumes that
+ * the other process understands this Tk message, otherwise
+ * our requested geometry will be ignored.
+ */
+
+ SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, width, height);
+ } else {
+ int reqHeight, reqWidth;
+ RECT windowRect;
+ int menuInc = GetSystemMetrics(SM_CYMENU);
+ int newHeight;
+
+ /*
+ * We have to keep resizing the window until we get the
+ * requested height in the client area. If the client
+ * area has zero height, then the window rect is too
+ * small by definition. Try increasing the border height
+ * and try again. Once we have a positive size, then
+ * we can adjust the height exactly. If the window
+ * rect comes back smaller than we requested, we have
+ * hit the maximum constraints that Windows imposes.
+ * Once we find a positive client size, the next size
+ * is the one we try no matter what.
+ */
+
+ reqHeight = height + wmPtr->borderHeight;
+ reqWidth = width + wmPtr->borderWidth;
+
+ while (1) {
+ MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
+ GetWindowRect(wmPtr->wrapper, &windowRect);
+ newHeight = windowRect.bottom - windowRect.top;
+
+ /*
+ * If the request wasn't satisfied, we have hit an external
+ * constraint and must stop.
+ */
+
+ if (newHeight < reqHeight) {
+ break;
+ }
+
+ /*
+ * Now check the size of the client area against our ideal.
+ */
+
+ GetClientRect(wmPtr->wrapper, &windowRect);
+ newHeight = windowRect.bottom - windowRect.top;
+
+ if (newHeight == height) {
+ /*
+ * We're done.
+ */
+ break;
+ } else if (newHeight > height) {
+ /*
+ * One last resize to get rid of the extra space.
+ */
+ menuInc = newHeight - height;
+ reqHeight -= menuInc;
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ y += menuInc;
+ }
+ MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE);
+ break;
+ }
+
+ /*
+ * We didn't get enough space to satisfy our requested
+ * height, so the menu must have wrapped. Increase the
+ * size of the window by one menu height and move the
+ * window if it is positioned relative to the lower right
+ * corner of the screen.
+ */
+
+ reqHeight += menuInc;
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ y -= menuInc;
+ }
+ }
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ DrawMenuBar(wmPtr->wrapper);
+ }
+ }
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseGeometry --
+ *
+ * This procedure parses a geometry string and updates
+ * information used to control the geometry of a top-level
+ * window.
+ *
+ * Results:
+ * A standard Tcl return value, plus an error message in
+ * interp->result if an error occurs.
+ *
+ * Side effects:
+ * The size and/or location of winPtr may change.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ParseGeometry(interp, string, winPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ char *string; /* String containing new geometry. Has the
+ * standard form "=wxh+x+y". */
+ TkWindow *winPtr; /* Pointer to top-level window whose
+ * geometry is to be changed. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ int x, y, width, height, flags;
+ char *end;
+ register char *p = string;
+
+ /*
+ * The leading "=" is optional.
+ */
+
+ if (*p == '=') {
+ p++;
+ }
+
+ /*
+ * Parse the width and height, if they are present. Don't
+ * actually update any of the fields of wmPtr until we've
+ * successfully parsed the entire geometry string.
+ */
+
+ width = wmPtr->width;
+ height = wmPtr->height;
+ x = wmPtr->x;
+ y = wmPtr->y;
+ flags = wmPtr->flags;
+ if (isdigit(UCHAR(*p))) {
+ width = strtoul(p, &end, 10);
+ p = end;
+ if (*p != 'x') {
+ goto error;
+ }
+ p++;
+ if (!isdigit(UCHAR(*p))) {
+ goto error;
+ }
+ height = strtoul(p, &end, 10);
+ p = end;
+ }
+
+ /*
+ * Parse the X and Y coordinates, if they are present.
+ */
+
+ if (*p != '\0') {
+ flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
+ if (*p == '-') {
+ flags |= WM_NEGATIVE_X;
+ } else if (*p != '+') {
+ goto error;
+ }
+ p++;
+ if (!isdigit(UCHAR(*p)) && (*p != '-')) {
+ goto error;
+ }
+ x = strtol(p, &end, 10);
+ p = end;
+ if (*p == '-') {
+ flags |= WM_NEGATIVE_Y;
+ } else if (*p != '+') {
+ goto error;
+ }
+ p++;
+ if (!isdigit(UCHAR(*p)) && (*p != '-')) {
+ goto error;
+ }
+ y = strtol(p, &end, 10);
+ if (*end != '\0') {
+ goto error;
+ }
+
+ /*
+ * Assume that the geometry information came from the user,
+ * unless an explicit source has been specified. Otherwise
+ * most window managers assume that the size hints were
+ * program-specified and they ignore them.
+ */
+
+ if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
+ wmPtr->sizeHintsFlags |= USPosition;
+ }
+ }
+
+ /*
+ * Everything was parsed OK. Update the fields of *wmPtr and
+ * arrange for the appropriate information to be percolated out
+ * to the window manager at the next idle moment.
+ */
+
+ wmPtr->width = width;
+ wmPtr->height = height;
+ wmPtr->x = x;
+ wmPtr->y = y;
+ flags |= WM_MOVE_PENDING;
+ wmPtr->flags = flags;
+
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+ return TCL_OK;
+
+ error:
+ Tcl_AppendResult(interp, "bad geometry specifier \"",
+ string, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetRootCoords --
+ *
+ * Given a token for a window, this procedure traces through the
+ * window's lineage to find the (virtual) root-window coordinates
+ * corresponding to point (0,0) in the window.
+ *
+ * Results:
+ * The locations pointed to by xPtr and yPtr are filled in with
+ * the root coordinates of the (0,0) point in tkwin.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_GetRootCoords(tkwin, xPtr, yPtr)
+ Tk_Window tkwin; /* Token for window. */
+ int *xPtr; /* Where to store x-displacement of (0,0). */
+ int *yPtr; /* Where to store y-displacement of (0,0). */
+{
+ register TkWindow *winPtr = (TkWindow *) tkwin;
+
+ /*
+ * If the window is mapped, let Windows figure out the translation.
+ */
+
+ if (winPtr->window != None) {
+ HWND hwnd = Tk_GetHWND(winPtr->window);
+ POINT point;
+
+ point.x = 0;
+ point.y = 0;
+
+ ClientToScreen(hwnd, &point);
+
+ *xPtr = point.x;
+ *yPtr = point.y;
+ } else {
+ *xPtr = 0;
+ *yPtr = 0;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_CoordsToWindow --
+ *
+ * Given the (virtual) root coordinates of a point, this procedure
+ * returns the token for the top-most window covering that point,
+ * if there exists such a window in this application.
+ *
+ * Results:
+ * The return result is either a token for the window corresponding
+ * to rootX and rootY, or else NULL to indicate that there is no such
+ * window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Tk_Window
+Tk_CoordsToWindow(rootX, rootY, tkwin)
+ int rootX, rootY; /* Coordinates of point in root window. If
+ * a virtual-root window manager is in use,
+ * these coordinates refer to the virtual
+ * root, not the real root. */
+ Tk_Window tkwin; /* Token for any window in application;
+ * used to identify the display. */
+{
+ POINT pos;
+ HWND hwnd;
+ TkWindow *winPtr;
+
+ pos.x = rootX;
+ pos.y = rootY;
+ hwnd = WindowFromPoint(pos);
+
+ winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd);
+ if (winPtr && (winPtr->mainPtr == ((TkWindow *) tkwin)->mainPtr)) {
+ return (Tk_Window) winPtr;
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetVRootGeometry --
+ *
+ * This procedure returns information about the virtual root
+ * window corresponding to a particular Tk window.
+ *
+ * Results:
+ * The values at xPtr, yPtr, widthPtr, and heightPtr are set
+ * with the offset and dimensions of the root window corresponding
+ * to tkwin. If tkwin is being managed by a virtual root window
+ * manager these values correspond to the virtual root window being
+ * used for tkwin; otherwise the offsets will be 0 and the
+ * dimensions will be those of the screen.
+ *
+ * Side effects:
+ * Vroot window information is refreshed if it is out of date.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
+ Tk_Window tkwin; /* Window whose virtual root is to be
+ * queried. */
+ int *xPtr, *yPtr; /* Store x and y offsets of virtual root
+ * here. */
+ int *widthPtr, *heightPtr; /* Store dimensions of virtual root here. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+
+ *xPtr = 0;
+ *yPtr = 0;
+ *widthPtr = DisplayWidth(winPtr->display, winPtr->screenNum);
+ *heightPtr = DisplayHeight(winPtr->display, winPtr->screenNum);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MoveToplevelWindow --
+ *
+ * This procedure is called instead of Tk_MoveWindow to adjust
+ * the x-y location of a top-level window. It delays the actual
+ * move to a later time and keeps window-manager information
+ * up-to-date with the move
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is eventually moved so that its upper-left corner
+ * (actually, the upper-left corner of the window's decorative
+ * frame, if there is one) is at (x,y).
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_MoveToplevelWindow(tkwin, x, y)
+ Tk_Window tkwin; /* Window to move. */
+ int x, y; /* New location for window (within
+ * parent). */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ panic("Tk_MoveToplevelWindow called with non-toplevel window");
+ }
+ wmPtr->x = x;
+ wmPtr->y = y;
+ wmPtr->flags |= WM_MOVE_PENDING;
+ wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
+ if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
+ wmPtr->sizeHintsFlags |= USPosition;
+ }
+
+ /*
+ * If the window has already been mapped, must bring its geometry
+ * up-to-date immediately, otherwise an event might arrive from the
+ * server that would overwrite wmPtr->x and wmPtr->y and lose the
+ * new position.
+ */
+
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ if (wmPtr->flags & WM_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
+ }
+ UpdateGeometryInfo((ClientData) winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmProtocolEventProc --
+ *
+ * This procedure is called by the Tk_HandleEvent whenever a
+ * ClientMessage event arrives whose type is "WM_PROTOCOLS".
+ * This procedure handles the message from the window manager
+ * in an appropriate fashion.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Depends on what sort of handler, if any, was set up for the
+ * protocol.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWmProtocolEventProc(winPtr, eventPtr)
+ TkWindow *winPtr; /* Window to which the event was sent. */
+ XEvent *eventPtr; /* X event. */
+{
+ WmInfo *wmPtr;
+ register ProtocolHandler *protPtr;
+ Atom protocol;
+ int result;
+ Tcl_Interp *interp;
+
+ wmPtr = winPtr->wmInfoPtr;
+ if (wmPtr == NULL) {
+ return;
+ }
+ protocol = (Atom) eventPtr->xclient.data.l[0];
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ if (protocol == protPtr->protocol) {
+ Tcl_Preserve((ClientData) protPtr);
+ interp = protPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ result = Tcl_GlobalEval(interp, protPtr->command);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (command for \"");
+ Tcl_AddErrorInfo(interp,
+ Tk_GetAtomName((Tk_Window) winPtr, protocol));
+ Tcl_AddErrorInfo(interp, "\" window manager protocol)");
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_Release((ClientData) interp);
+ Tcl_Release((ClientData) protPtr);
+ return;
+ }
+ }
+
+ /*
+ * No handler was present for this protocol. If this is a
+ * WM_DELETE_WINDOW message then just destroy the window.
+ */
+
+ if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
+ Tk_DestroyWindow((Tk_Window) winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmRestackToplevel --
+ *
+ * This procedure restacks a top-level window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * WinPtr gets restacked as specified by aboveBelow and otherPtr.
+ * This procedure doesn't return until the restack has taken
+ * effect and the ConfigureNotify event for it has been received.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
+ TkWindow *winPtr; /* Window to restack. */
+ int aboveBelow; /* Gives relative position for restacking;
+ * must be Above or Below. */
+ TkWindow *otherPtr; /* Window relative to which to restack;
+ * if NULL, then winPtr gets restacked
+ * above or below *all* siblings. */
+{
+ HWND hwnd, insertAfter;
+
+ /*
+ * Can't set stacking order properly until the window is on the
+ * screen (mapping it may give it a reparent window).
+ */
+
+ if (winPtr->window == None) {
+ Tk_MakeWindowExist((Tk_Window) winPtr);
+ }
+ if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
+ TkWmMapWindow(winPtr);
+ }
+ hwnd = (winPtr->wmInfoPtr->wrapper != NULL)
+ ? winPtr->wmInfoPtr->wrapper : Tk_GetHWND(winPtr->window);
+
+
+ if (otherPtr != NULL) {
+ if (otherPtr->window == None) {
+ Tk_MakeWindowExist((Tk_Window) otherPtr);
+ }
+ if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
+ TkWmMapWindow(otherPtr);
+ }
+ insertAfter = (otherPtr->wmInfoPtr->wrapper != NULL)
+ ? otherPtr->wmInfoPtr->wrapper : Tk_GetHWND(otherPtr->window);
+ } else {
+ insertAfter = NULL;
+ }
+
+ TkWinSetWindowPos(hwnd, insertAfter, aboveBelow);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmAddToColormapWindows --
+ *
+ * This procedure is called to add a given window to the
+ * WM_COLORMAP_WINDOWS property for its top-level, if it
+ * isn't already there. It is invoked by the Tk code that
+ * creates a new colormap, in order to make sure that colormap
+ * information is propagated to the window manager by default.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * WinPtr's window gets added to the WM_COLORMAP_WINDOWS
+ * property of its nearest top-level ancestor, unless the
+ * colormaps have been set explicitly with the
+ * "wm colormapwindows" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWmAddToColormapWindows(winPtr)
+ TkWindow *winPtr; /* Window with a non-default colormap.
+ * Should not be a top-level window. */
+{
+ TkWindow *topPtr;
+ TkWindow **oldPtr, **newPtr;
+ int count, i;
+
+ if (winPtr->window == None) {
+ return;
+ }
+
+ for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
+ if (topPtr == NULL) {
+ /*
+ * Window is being deleted. Skip the whole operation.
+ */
+
+ return;
+ }
+ if (topPtr->flags & TK_TOP_LEVEL) {
+ break;
+ }
+ }
+ if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
+ return;
+ }
+
+ /*
+ * Make sure that the window isn't already in the list.
+ */
+
+ count = topPtr->wmInfoPtr->cmapCount;
+ oldPtr = topPtr->wmInfoPtr->cmapList;
+
+ for (i = 0; i < count; i++) {
+ if (oldPtr[i] == winPtr) {
+ return;
+ }
+ }
+
+ /*
+ * Make a new bigger array and use it to reset the property.
+ * Automatically add the toplevel itself as the last element
+ * of the list.
+ */
+
+ newPtr = (TkWindow **) ckalloc((unsigned) ((count+2)*sizeof(TkWindow*)));
+ if (count > 0) {
+ memcpy(newPtr, oldPtr, count * sizeof(TkWindow*));
+ }
+ if (count == 0) {
+ count++;
+ }
+ newPtr[count-1] = winPtr;
+ newPtr[count] = topPtr;
+ if (oldPtr != NULL) {
+ ckfree((char *) oldPtr);
+ }
+
+ topPtr->wmInfoPtr->cmapList = newPtr;
+ topPtr->wmInfoPtr->cmapCount = count+1;
+
+ /*
+ * Now we need to force the updated colormaps to be installed.
+ */
+
+ if (topPtr->wmInfoPtr == foregroundWmPtr) {
+ InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_QUERYNEWPALETTE, 1);
+ } else {
+ InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_PALETTECHANGED, 0);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmRemoveFromColormapWindows --
+ *
+ * This procedure is called to remove a given window from the
+ * WM_COLORMAP_WINDOWS property for its top-level. It is invoked
+ * when windows are deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
+ * property of its nearest top-level ancestor, unless the
+ * top-level itself is being deleted too.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWmRemoveFromColormapWindows(winPtr)
+ TkWindow *winPtr; /* Window that may be present in
+ * WM_COLORMAP_WINDOWS property for its
+ * top-level. Should not be a top-level
+ * window. */
+{
+ TkWindow *topPtr;
+ TkWindow **oldPtr;
+ int count, i, j;
+
+ for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
+ if (topPtr == NULL) {
+ /*
+ * Ancestors have been deleted, so skip the whole operation.
+ * Seems like this can't ever happen?
+ */
+
+ return;
+ }
+ if (topPtr->flags & TK_TOP_LEVEL) {
+ break;
+ }
+ }
+ if (topPtr->flags & TK_ALREADY_DEAD) {
+ /*
+ * Top-level is being deleted, so there's no need to cleanup
+ * the WM_COLORMAP_WINDOWS property.
+ */
+
+ return;
+ }
+
+ /*
+ * Find the window and slide the following ones down to cover
+ * it up.
+ */
+
+ count = topPtr->wmInfoPtr->cmapCount;
+ oldPtr = topPtr->wmInfoPtr->cmapList;
+ for (i = 0; i < count; i++) {
+ if (oldPtr[i] == winPtr) {
+ for (j = i ; j < count-1; j++) {
+ oldPtr[j] = oldPtr[j+1];
+ }
+ topPtr->wmInfoPtr->cmapCount = count-1;
+ break;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinSetMenu--
+ *
+ * Associcates a given HMENU to a window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu will end up being drawn in the window, and the geometry
+ * of the window will have to be changed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinSetMenu(tkwin, hMenu)
+ Tk_Window tkwin; /* the window to put the menu in */
+ HMENU hMenu; /* the menu to set */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ wmPtr->hMenu = hMenu;
+
+ if (!(wmPtr->flags & TK_EMBEDDED)) {
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ int syncPending = wmPtr->flags & WM_SYNC_PENDING;
+
+ wmPtr->flags |= WM_SYNC_PENDING;
+ SetMenu(wmPtr->wrapper, hMenu);
+ if (!syncPending) {
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+ }
+ }
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING|WM_MOVE_PENDING;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureTopLevel --
+ *
+ * Generate a ConfigureNotify event based on the current position
+ * information. This procedure is called by TopLevelProc.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Queues a new event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConfigureTopLevel(pos)
+ WINDOWPOS *pos;
+{
+ TkWindow *winPtr = GetTopLevel(pos->hwnd);
+ WmInfo *wmPtr;
+ int state; /* Current window state. */
+ RECT rect;
+ WINDOWPLACEMENT windowPos;
+
+ if (winPtr == NULL) {
+ return;
+ }
+
+ wmPtr = winPtr->wmInfoPtr;
+
+ /*
+ * Determine the current window state.
+ */
+
+ if (!IsWindowVisible(wmPtr->wrapper)) {
+ state = WithdrawnState;
+ } else {
+ windowPos.length = sizeof(WINDOWPLACEMENT);
+ GetWindowPlacement(wmPtr->wrapper, &windowPos);
+ switch (windowPos.showCmd) {
+ case SW_SHOWMAXIMIZED:
+ state = ZoomState;
+ break;
+ case SW_SHOWMINIMIZED:
+ state = IconicState;
+ break;
+ case SW_SHOWNORMAL:
+ state = NormalState;
+ break;
+ }
+ }
+
+ /*
+ * If the state of the window just changed, be sure to update the
+ * child window information.
+ */
+
+ if (wmPtr->hints.initial_state != state) {
+ wmPtr->hints.initial_state = state;
+ switch (state) {
+ case WithdrawnState:
+ case IconicState:
+ XUnmapWindow(winPtr->display, winPtr->window);
+ break;
+
+ case NormalState:
+ /*
+ * Schedule a geometry update. Since we ignore geometry
+ * requests while in any other state, the geometry info
+ * may be stale.
+ */
+
+ if (!(wmPtr->flags & WM_UPDATE_PENDING)) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo,
+ (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+ /* fall through */
+ case ZoomState:
+ XMapWindow(winPtr->display, winPtr->window);
+ pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
+ break;
+ }
+ }
+
+ /*
+ * Don't report geometry changes in the Iconic or Withdrawn states.
+ */
+
+ if (state == WithdrawnState || state == IconicState) {
+ return;
+ }
+
+
+ /*
+ * Compute the current geometry of the client area, reshape the
+ * Tk window and generate a ConfigureNotify event.
+ */
+
+ GetClientRect(wmPtr->wrapper, &rect);
+ winPtr->changes.x = pos->x;
+ winPtr->changes.y = pos->y;
+ winPtr->changes.width = rect.right - rect.left;
+ winPtr->changes.height = rect.bottom - rect.top;
+ wmPtr->borderHeight = pos->cy - winPtr->changes.height;
+ MoveWindow(Tk_GetHWND(winPtr->window), 0, 0,
+ winPtr->changes.width, winPtr->changes.height, TRUE);
+ GenerateConfigureNotify(winPtr);
+
+ /*
+ * Update window manager geometry info if needed.
+ */
+
+ if (state == NormalState) {
+
+ /*
+ * Update size information from the event. There are a couple of
+ * tricky points here:
+ *
+ * 1. If the user changed the size externally then set wmPtr->width
+ * and wmPtr->height just as if a "wm geometry" command had been
+ * invoked with the same information.
+ * 2. However, if the size is changing in response to a request
+ * coming from us (sync is set), then don't set
+ * wmPtr->width or wmPtr->height (otherwise the window will stop
+ * tracking geometry manager requests).
+ */
+
+ if (!(wmPtr->flags & WM_SYNC_PENDING)) {
+ if (!(pos->flags & SWP_NOSIZE)) {
+ if ((wmPtr->width == -1)
+ && (winPtr->changes.width == winPtr->reqWidth)) {
+ /*
+ * Don't set external width, since the user didn't
+ * change it from what the widgets asked for.
+ */
+ } else {
+ if (wmPtr->gridWin != NULL) {
+ wmPtr->width = wmPtr->reqGridWidth
+ + (winPtr->changes.width - winPtr->reqWidth)
+ / wmPtr->widthInc;
+ if (wmPtr->width < 0) {
+ wmPtr->width = 0;
+ }
+ } else {
+ wmPtr->width = winPtr->changes.width;
+ }
+ }
+ if ((wmPtr->height == -1)
+ && (winPtr->changes.height == winPtr->reqHeight)) {
+ /*
+ * Don't set external height, since the user didn't change
+ * it from what the widgets asked for.
+ */
+ } else {
+ if (wmPtr->gridWin != NULL) {
+ wmPtr->height = wmPtr->reqGridHeight
+ + (winPtr->changes.height - winPtr->reqHeight)
+ / wmPtr->heightInc;
+ if (wmPtr->height < 0) {
+ wmPtr->height = 0;
+ }
+ } else {
+ wmPtr->height = winPtr->changes.height;
+ }
+ }
+ wmPtr->configWidth = winPtr->changes.width;
+ wmPtr->configHeight = winPtr->changes.height;
+ }
+ /*
+ * If the user moved the window, we should switch back
+ * to normal coordinates.
+ */
+
+ if (!(pos->flags & SWP_NOMOVE)) {
+ wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
+ }
+ }
+
+ /*
+ * Update the wrapper window location information.
+ */
+
+ if (wmPtr->flags & WM_NEGATIVE_X) {
+ wmPtr->x = DisplayWidth(winPtr->display, winPtr->screenNum)
+ - winPtr->changes.x - (winPtr->changes.width
+ + wmPtr->borderWidth);
+ } else {
+ wmPtr->x = winPtr->changes.x;
+ }
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ wmPtr->y = DisplayHeight(winPtr->display, winPtr->screenNum)
+ - winPtr->changes.y - (winPtr->changes.height
+ + wmPtr->borderHeight);
+ } else {
+ wmPtr->y = winPtr->changes.y;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateConfigureNotify --
+ *
+ * Generate a ConfigureNotify event from the current geometry
+ * information for the specified toplevel window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sends an X event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GenerateConfigureNotify(winPtr)
+ TkWindow *winPtr;
+{
+ XEvent event;
+
+ /*
+ * Generate a ConfigureNotify event.
+ */
+
+ event.type = ConfigureNotify;
+ event.xconfigure.serial = winPtr->display->request;
+ event.xconfigure.send_event = False;
+ event.xconfigure.display = winPtr->display;
+ event.xconfigure.event = winPtr->window;
+ event.xconfigure.window = winPtr->window;
+ event.xconfigure.border_width = winPtr->changes.border_width;
+ event.xconfigure.override_redirect = winPtr->atts.override_redirect;
+ event.xconfigure.x = winPtr->changes.x;
+ event.xconfigure.y = winPtr->changes.y;
+ event.xconfigure.width = winPtr->changes.width;
+ event.xconfigure.height = winPtr->changes.height;
+ event.xconfigure.above = None;
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InstallColormaps --
+ *
+ * Installs the colormaps associated with the toplevel which is
+ * currently active.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * May change the system palette and generate damage.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InstallColormaps(hwnd, message, isForemost)
+ HWND hwnd; /* Toplevel wrapper window whose colormaps
+ * should be installed. */
+ int message; /* Either WM_PALETTECHANGED or
+ * WM_QUERYNEWPALETTE */
+ int isForemost; /* 1 if window is foremost, else 0 */
+{
+ int i;
+ HDC dc;
+ HPALETTE oldPalette;
+ TkWindow *winPtr = GetTopLevel(hwnd);
+ WmInfo *wmPtr;
+
+ if (winPtr == NULL) {
+ return 0;
+ }
+
+ wmPtr = winPtr->wmInfoPtr;
+
+ if (message == WM_QUERYNEWPALETTE) {
+ /*
+ * Case 1: This window is about to become the foreground window, so we
+ * need to install the primary palette. If the system palette was
+ * updated, then Windows will generate a WM_PALETTECHANGED message.
+ * Otherwise, we have to synthesize one in order to ensure that the
+ * secondary palettes are installed properly.
+ */
+
+ foregroundWmPtr = wmPtr;
+
+ if (wmPtr->cmapCount > 0) {
+ winPtr = wmPtr->cmapList[0];
+ }
+
+ systemPalette = TkWinGetPalette(winPtr->atts.colormap);
+ dc = GetDC(hwnd);
+ oldPalette = SelectPalette(dc, systemPalette, FALSE);
+ if (RealizePalette(dc)) {
+ RefreshColormap(winPtr->atts.colormap);
+ } else if (wmPtr->cmapCount > 1) {
+ SelectPalette(dc, oldPalette, TRUE);
+ RealizePalette(dc);
+ ReleaseDC(hwnd, dc);
+ SendMessage(hwnd, WM_PALETTECHANGED, (WPARAM)hwnd,
+ (LPARAM)NULL);
+ return TRUE;
+ }
+
+ } else {
+ /*
+ * Window is being notified of a change in the system palette.
+ * If this window is the foreground window, then we should only
+ * install the secondary palettes, since the primary was installed
+ * in response to the WM_QUERYPALETTE message. Otherwise, install
+ * all of the palettes.
+ */
+
+
+ if (!isForemost) {
+ if (wmPtr->cmapCount > 0) {
+ winPtr = wmPtr->cmapList[0];
+ }
+ i = 1;
+ } else {
+ if (wmPtr->cmapCount <= 1) {
+ return TRUE;
+ }
+ winPtr = wmPtr->cmapList[1];
+ i = 2;
+ }
+ dc = GetDC(hwnd);
+ oldPalette = SelectPalette(dc,
+ TkWinGetPalette(winPtr->atts.colormap), TRUE);
+ if (RealizePalette(dc)) {
+ RefreshColormap(winPtr->atts.colormap);
+ }
+ for (; i < wmPtr->cmapCount; i++) {
+ winPtr = wmPtr->cmapList[i];
+ SelectPalette(dc, TkWinGetPalette(winPtr->atts.colormap), TRUE);
+ if (RealizePalette(dc)) {
+ RefreshColormap(winPtr->atts.colormap);
+ }
+ }
+ }
+
+ SelectPalette(dc, oldPalette, TRUE);
+ RealizePalette(dc);
+ ReleaseDC(hwnd, dc);
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RefreshColormap --
+ *
+ * This function is called to force all of the windows that use
+ * a given colormap to redraw themselves. The quickest way to
+ * do this is to iterate over the toplevels, looking in the
+ * cmapList for matches. This will quickly eliminate subtrees
+ * that don't use a given colormap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Causes damage events to be generated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RefreshColormap(colormap)
+ Colormap colormap;
+{
+ WmInfo *wmPtr;
+ int i;
+
+ for (wmPtr = firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
+ if (wmPtr->cmapCount > 0) {
+ for (i = 0; i < wmPtr->cmapCount; i++) {
+ if ((wmPtr->cmapList[i]->atts.colormap == colormap)
+ && Tk_IsMapped(wmPtr->cmapList[i])) {
+ InvalidateSubTree(wmPtr->cmapList[i], colormap);
+ }
+ }
+ } else if ((wmPtr->winPtr->atts.colormap == colormap)
+ && Tk_IsMapped(wmPtr->winPtr)) {
+ InvalidateSubTree(wmPtr->winPtr, colormap);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InvalidateSubTree --
+ *
+ * This function recursively generates damage for a window and
+ * all of its mapped children that belong to the same toplevel and
+ * are using the specified colormap.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Generates damage for the specified subtree.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+InvalidateSubTree(winPtr, colormap)
+ TkWindow *winPtr;
+ Colormap colormap;
+{
+ TkWindow *childPtr;
+
+ /*
+ * Generate damage for the current window if it is using the
+ * specified colormap.
+ */
+
+ if (winPtr->atts.colormap == colormap) {
+ InvalidateRect(Tk_GetHWND(winPtr->window), NULL, FALSE);
+ }
+
+ for (childPtr = winPtr->childList; childPtr != NULL;
+ childPtr = childPtr->nextPtr) {
+ /*
+ * We can stop the descent when we hit an unmapped or
+ * toplevel window.
+ */
+
+ if (!Tk_IsTopLevel(childPtr) && Tk_IsMapped(childPtr)) {
+ InvalidateSubTree(childPtr, colormap);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetSystemPalette --
+ *
+ * Retrieves the currently installed foreground palette.
+ *
+ * Results:
+ * Returns the global foreground palette, if there is one.
+ * Otherwise, returns NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HPALETTE
+TkWinGetSystemPalette()
+{
+ return systemPalette;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMinSize --
+ *
+ * This procedure computes the current minWidth and minHeight
+ * values for a window, taking into account the possibility
+ * that they may be defaulted.
+ *
+ * Results:
+ * The values at *minWidthPtr and *minHeightPtr are filled
+ * in with the minimum allowable dimensions of wmPtr's window,
+ * in grid units. If the requested minimum is smaller than the
+ * system required minimum, then this procedure computes the
+ * smallest size that will satisfy both the system and the
+ * grid constraints.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMinSize(wmPtr, minWidthPtr, minHeightPtr)
+ WmInfo *wmPtr; /* Window manager information for the
+ * window. */
+ int *minWidthPtr; /* Where to store the current minimum
+ * width of the window. */
+ int *minHeightPtr; /* Where to store the current minimum
+ * height of the window. */
+{
+ int tmp, base;
+ TkWindow *winPtr = wmPtr->winPtr;
+
+ /*
+ * Compute the minimum width by taking the default client size
+ * and rounding it up to the nearest grid unit. Return the greater
+ * of the default minimum and the specified minimum.
+ */
+
+ tmp = wmPtr->defMinWidth - wmPtr->borderWidth;
+ if (tmp < 0) {
+ tmp = 0;
+ }
+ if (wmPtr->gridWin != NULL) {
+ base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc);
+ if (base < 0) {
+ base = 0;
+ }
+ tmp = ((tmp - base) + wmPtr->widthInc - 1)/wmPtr->widthInc;
+ }
+ if (tmp < wmPtr->minWidth) {
+ tmp = wmPtr->minWidth;
+ }
+ *minWidthPtr = tmp;
+
+ /*
+ * Compute the minimum height in a similar fashion.
+ */
+
+ tmp = wmPtr->defMinHeight - wmPtr->borderHeight;
+ if (tmp < 0) {
+ tmp = 0;
+ }
+ if (wmPtr->gridWin != NULL) {
+ base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc);
+ if (base < 0) {
+ base = 0;
+ }
+ tmp = ((tmp - base) + wmPtr->heightInc - 1)/wmPtr->heightInc;
+ }
+ if (tmp < wmPtr->minHeight) {
+ tmp = wmPtr->minHeight;
+ }
+ *minHeightPtr = tmp;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMaxSize --
+ *
+ * This procedure computes the current maxWidth and maxHeight
+ * values for a window, taking into account the possibility
+ * that they may be defaulted.
+ *
+ * Results:
+ * The values at *maxWidthPtr and *maxHeightPtr are filled
+ * in with the maximum allowable dimensions of wmPtr's window,
+ * in grid units. If no maximum has been specified for the
+ * window, then this procedure computes the largest sizes that
+ * will fit on the screen.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)
+ WmInfo *wmPtr; /* Window manager information for the
+ * window. */
+ int *maxWidthPtr; /* Where to store the current maximum
+ * width of the window. */
+ int *maxHeightPtr; /* Where to store the current maximum
+ * height of the window. */
+{
+ int tmp;
+
+ if (wmPtr->maxWidth > 0) {
+ *maxWidthPtr = wmPtr->maxWidth;
+ } else {
+ /*
+ * Must compute a default width. Fill up the display, leaving a
+ * bit of extra space for the window manager's borders.
+ */
+
+ tmp = wmPtr->defMaxWidth - wmPtr->borderWidth;
+ if (wmPtr->gridWin != NULL) {
+ /*
+ * Gridding is turned on; convert from pixels to grid units.
+ */
+
+ tmp = wmPtr->reqGridWidth
+ + (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc;
+ }
+ *maxWidthPtr = tmp;
+ }
+ if (wmPtr->maxHeight > 0) {
+ *maxHeightPtr = wmPtr->maxHeight;
+ } else {
+ tmp = wmPtr->defMaxHeight - wmPtr->borderHeight;
+ if (wmPtr->gridWin != NULL) {
+ tmp = wmPtr->reqGridHeight
+ + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;
+ }
+ *maxHeightPtr = tmp;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TopLevelProc --
+ *
+ * Callback from Windows whenever an event occurs on a top level
+ * window.
+ *
+ * Results:
+ * Standard Windows return value.
+ *
+ * Side effects:
+ * Default window behavior.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static LRESULT CALLBACK
+TopLevelProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ if (message == WM_WINDOWPOSCHANGED) {
+ WINDOWPOS *pos = (WINDOWPOS *) lParam;
+ TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(pos->hwnd);
+
+ if (winPtr == NULL) {
+ return 0;
+ }
+
+ /*
+ * Update the shape of the contained window.
+ */
+
+ if (!(pos->flags & SWP_NOSIZE)) {
+ winPtr->changes.width = pos->cx;
+ winPtr->changes.height = pos->cy;
+ }
+ if (!(pos->flags & SWP_NOMOVE)) {
+ winPtr->changes.x = pos->x;
+ winPtr->changes.y = pos->y;
+ }
+
+ GenerateConfigureNotify(winPtr);
+
+ Tcl_ServiceAll();
+ return 0;
+ }
+ return TkWinChildProc(hwnd, message, wParam, lParam);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WmProc --
+ *
+ * Callback from Windows whenever an event occurs on the decorative
+ * frame.
+ *
+ * Results:
+ * Standard Windows return value.
+ *
+ * Side effects:
+ * Default window behavior.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static LRESULT CALLBACK
+WmProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ static int inMoveSize = 0;
+ static oldMode; /* This static is set upon entering move/size mode
+ * and is used to reset the service mode after
+ * leaving move/size mode. Note that this mechanism
+ * assumes move/size is only one level deep. */
+ LRESULT result;
+ TkWindow *winPtr;
+
+ if (TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &result)) {
+ goto done;
+ }
+
+ switch (message) {
+ case WM_KILLFOCUS:
+ case WM_ERASEBKGND:
+ result = 0;
+ goto done;
+
+ case WM_ENTERSIZEMOVE:
+ inMoveSize = 1;
+ oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
+ break;
+
+ case WM_ACTIVATE:
+ case WM_EXITSIZEMOVE:
+ if (inMoveSize) {
+ inMoveSize = 0;
+ Tcl_SetServiceMode(oldMode);
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ SetLimits(hwnd, (MINMAXINFO *) lParam);
+ result = 0;
+ goto done;
+
+ case WM_PALETTECHANGED:
+ result = InstallColormaps(hwnd, WM_PALETTECHANGED,
+ hwnd == (HWND)wParam);
+ goto done;
+
+ case WM_QUERYNEWPALETTE:
+ result = InstallColormaps(hwnd, WM_QUERYNEWPALETTE, TRUE);
+ goto done;
+
+ case WM_WINDOWPOSCHANGED:
+ ConfigureTopLevel((WINDOWPOS *) lParam);
+ result = 0;
+ goto done;
+
+ default:
+ break;
+ }
+
+ winPtr = GetTopLevel(hwnd);
+ if (winPtr && winPtr->window) {
+ HWND child = Tk_GetHWND(winPtr->window);
+ if (message == WM_SETFOCUS) {
+ SetFocus(child);
+ result = 0;
+ } else if (!Tk_TranslateWinEvent(child, message, wParam, lParam,
+ &result)) {
+ result = DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ } else {
+ result = DefWindowProc(hwnd, message, wParam, lParam);
+ }
+
+ done:
+ Tcl_ServiceAll();
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMakeMenuWindow --
+ *
+ * Configure the window to be either a pull-down (or pop-up)
+ * menu, or as a toplevel (torn-off) menu or palette.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Changes the style bit used to create a new Mac toplevel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMakeMenuWindow(tkwin, transient)
+ Tk_Window tkwin; /* New window. */
+ int transient; /* 1 means menu is only posted briefly as
+ * a popup or pulldown or cascade. 0 means
+ * menu is always visible, e.g. as a torn-off
+ * menu. Determines whether save_under and
+ * override_redirect should be set. */
+{
+ XSetWindowAttributes atts;
+
+ if (transient) {
+ atts.override_redirect = True;
+ atts.save_under = True;
+ } else {
+ atts.override_redirect = False;
+ atts.save_under = False;
+ }
+
+ if ((atts.override_redirect != Tk_Attributes(tkwin)->override_redirect)
+ || (atts.save_under != Tk_Attributes(tkwin)->save_under)) {
+ Tk_ChangeWindowAttributes(tkwin,
+ CWOverrideRedirect|CWSaveUnder, &atts);
+ }
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinGetWrapperWindow --
+ *
+ * Gets the Windows HWND for a given window.
+ *
+ * Results:
+ * Returns the wrapper window for a Tk window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HWND
+TkWinGetWrapperWindow(
+ Tk_Window tkwin) /* The window we need the wrapper from */
+{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+ return (winPtr->wmInfoPtr->wrapper);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmFocusToplevel --
+ *
+ * This is a utility procedure invoked by focus-management code. It
+ * exists because of the extra wrapper windows that exist under
+ * Unix; its job is to map from wrapper windows to the
+ * corresponding toplevel windows. On PCs and Macs there are no
+ * wrapper windows so no mapping is necessary; this procedure just
+ * determines whether a window is a toplevel or not.
+ *
+ * Results:
+ * If winPtr is a toplevel window, returns the pointer to the
+ * window; otherwise returns NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkWindow *
+TkWmFocusToplevel(winPtr)
+ TkWindow *winPtr; /* Window that received a focus-related
+ * event. */
+{
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ return NULL;
+ }
+ return winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetWrapperWindow --
+ *
+ * This is a utility procedure invoked by focus-management code. It
+ * maps to the wrapper for a top-level, which is just the same
+ * as the top-level on Macs and PCs.
+ *
+ * Results:
+ * If winPtr is a toplevel window, returns the pointer to the
+ * window; otherwise returns NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkWindow *
+TkpGetWrapperWindow(
+ TkWindow *winPtr) /* Window that received a focus-related
+ * event. */
+{
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ return NULL;
+ }
+ return winPtr;
+}
diff --git a/win/tkWinX.c b/win/tkWinX.c
new file mode 100644
index 0000000..0b00186
--- /dev/null
+++ b/win/tkWinX.c
@@ -0,0 +1,1020 @@
+/*
+ * tkWinX.c --
+ *
+ * This file contains Windows emulation procedures for X routines.
+ *
+ * Copyright (c) 1995-1996 Sun Microsystems, Inc.
+ * Copyright (c) 1994 Software Research Associates, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkWinX.c 1.51 97/09/02 13:06:57
+ */
+
+#include "tkInt.h"
+#include "tkWinInt.h"
+
+/*
+ * Definitions of extern variables supplied by this file.
+ */
+
+int tkpIsWin32s = -1;
+
+/*
+ * Declarations of static variables used in this file.
+ */
+
+static HINSTANCE tkInstance = (HINSTANCE) NULL;
+ /* Global application instance handle. */
+static TkDisplay *winDisplay; /* Display that represents Windows screen. */
+static char winScreenName[] = ":0";
+ /* Default name of windows display. */
+static WNDCLASS childClass; /* Window class for child windows. */
+static childClassInitialized = 0; /* Registered child class? */
+
+/*
+ * Forward declarations of procedures used in this file.
+ */
+
+static void GenerateXEvent _ANSI_ARGS_((HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam));
+static unsigned int GetState _ANSI_ARGS_((UINT message, WPARAM wParam,
+ LPARAM lParam));
+static void GetTranslatedKey _ANSI_ARGS_((XKeyEvent *xkey));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetServerInfo --
+ *
+ * Given a window, this procedure returns information about
+ * the window server for that window. This procedure provides
+ * the guts of the "winfo server" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkGetServerInfo(interp, tkwin)
+ Tcl_Interp *interp; /* The server information is returned in
+ * this interpreter's result. */
+ Tk_Window tkwin; /* Token for window; this selects a
+ * particular display and server. */
+{
+ char buffer[50];
+ OSVERSIONINFO info;
+
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&info);
+ sprintf(buffer, "Windows %d.%d %d ", info.dwMajorVersion,
+ info.dwMinorVersion, info.dwBuildNumber);
+ Tcl_AppendResult(interp, buffer,
+ (info.dwPlatformId == VER_PLATFORM_WIN32s) ? "Win32s" : "Win32",
+ (char *) NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetHINSTANCE --
+ *
+ * Retrieves the global instance handle used by the Tk library.
+ *
+ * Results:
+ * Returns the global instance handle.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+HINSTANCE
+Tk_GetHINSTANCE()
+{
+ return tkInstance;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinXInit --
+ *
+ * Initialize Xlib emulation layer.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets up various data structures.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinXInit(hInstance)
+ HINSTANCE hInstance;
+{
+ OSVERSIONINFO info;
+
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&info);
+ tkpIsWin32s = (info.dwPlatformId == VER_PLATFORM_WIN32s);
+
+ if (childClassInitialized != 0) {
+ return;
+ }
+ childClassInitialized = 1;
+
+ tkInstance = hInstance;
+
+ childClass.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
+ childClass.cbClsExtra = 0;
+ childClass.cbWndExtra = 0;
+ childClass.hInstance = hInstance;
+ childClass.hbrBackground = NULL;
+ childClass.lpszMenuName = NULL;
+
+ /*
+ * Register the Child window class.
+ */
+
+ childClass.lpszClassName = TK_WIN_CHILD_CLASS_NAME;
+ childClass.lpfnWndProc = TkWinChildProc;
+ childClass.hIcon = NULL;
+ childClass.hCursor = NULL;
+
+ if (!RegisterClass(&childClass)) {
+ panic("Unable to register TkChild class");
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinXCleanup --
+ *
+ * Removes the registered classes for Tk.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Removes window classes from the system.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWinXCleanup(hInstance)
+ HINSTANCE hInstance;
+{
+ /*
+ * Clean up our own class.
+ */
+
+ if (childClassInitialized) {
+ childClassInitialized = 0;
+ UnregisterClass(TK_WIN_CHILD_CLASS_NAME, hInstance);
+ }
+
+ /*
+ * And let the window manager clean up its own class(es).
+ */
+
+ TkWinWmCleanup(hInstance);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetDefaultScreenName --
+ *
+ * Returns the name of the screen that Tk should use during
+ * initialization.
+ *
+ * Results:
+ * Returns a statically allocated string.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+TkGetDefaultScreenName(interp, screenName)
+ Tcl_Interp *interp; /* Not used. */
+ char *screenName; /* If NULL, use default string. */
+{
+ if ((screenName == NULL) || (screenName[0] == '\0')) {
+ screenName = winScreenName;
+ }
+ return screenName;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpOpenDisplay --
+ *
+ * Create the Display structure and fill it with device
+ * specific information.
+ *
+ * Results:
+ * Returns a Display structure on success or NULL on failure.
+ *
+ * Side effects:
+ * Allocates a new Display structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkDisplay *
+TkpOpenDisplay(display_name)
+ char *display_name;
+{
+ Screen *screen;
+ HDC dc;
+ TkWinDrawable *twdPtr;
+ Display *display;
+
+ if (winDisplay != NULL) {
+ if (strcmp(winDisplay->display->display_name, display_name) == 0) {
+ return winDisplay;
+ } else {
+ return NULL;
+ }
+ }
+
+ display = (Display *) ckalloc(sizeof(Display));
+ display->display_name = (char *) ckalloc(strlen(display_name)+1);
+ strcpy(display->display_name, display_name);
+
+ display->cursor_font = 1;
+ display->nscreens = 1;
+ display->request = 1;
+ display->qlen = 0;
+
+ screen = (Screen *) ckalloc(sizeof(Screen));
+ screen->display = display;
+
+ dc = GetDC(NULL);
+ screen->width = GetDeviceCaps(dc, HORZRES);
+ screen->height = GetDeviceCaps(dc, VERTRES);
+ screen->mwidth = MulDiv(screen->width, 254,
+ GetDeviceCaps(dc, LOGPIXELSX) * 10);
+ screen->mheight = MulDiv(screen->height, 254,
+ GetDeviceCaps(dc, LOGPIXELSY) * 10);
+
+ /*
+ * Set up the root window.
+ */
+
+ twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
+ if (twdPtr == NULL) {
+ return None;
+ }
+ twdPtr->type = TWD_WINDOW;
+ twdPtr->window.winPtr = NULL;
+ twdPtr->window.handle = NULL;
+ screen->root = (Window)twdPtr;
+
+ /*
+ * On windows, when creating a color bitmap, need two pieces of
+ * information: the number of color planes and the number of
+ * pixels per plane. Need to remember both quantities so that
+ * when constructing an HBITMAP for offscreen rendering, we can
+ * specify the correct value for the number of planes. Otherwise
+ * the HBITMAP won't be compatible with the HWND and we'll just
+ * get blank spots copied onto the screen.
+ */
+
+ screen->ext_data = (XExtData *) GetDeviceCaps(dc, PLANES);
+ screen->root_depth = GetDeviceCaps(dc, BITSPIXEL) * (int) screen->ext_data;
+
+ screen->root_visual = (Visual *) ckalloc(sizeof(Visual));
+ screen->root_visual->visualid = 0;
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) {
+ screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE);
+ screen->root_visual->class = PseudoColor;
+ screen->root_visual->red_mask = 0x0;
+ screen->root_visual->green_mask = 0x0;
+ screen->root_visual->blue_mask = 0x0;
+ } else {
+ if (screen->root_depth == 4) {
+ screen->root_visual->class = StaticColor;
+ screen->root_visual->map_entries = 16;
+ } else if (screen->root_depth == 8) {
+ screen->root_visual->class = StaticColor;
+ screen->root_visual->map_entries = 256;
+ } else if (screen->root_depth == 12) {
+ screen->root_visual->class = TrueColor;
+ screen->root_visual->map_entries = 32;
+ screen->root_visual->red_mask = 0xf0;
+ screen->root_visual->green_mask = 0xf000;
+ screen->root_visual->blue_mask = 0xf00000;
+ } else if (screen->root_depth == 16) {
+ screen->root_visual->class = TrueColor;
+ screen->root_visual->map_entries = 64;
+ screen->root_visual->red_mask = 0xf8;
+ screen->root_visual->green_mask = 0xfc00;
+ screen->root_visual->blue_mask = 0xf80000;
+ } else if (screen->root_depth >= 24) {
+ screen->root_visual->class = TrueColor;
+ screen->root_visual->map_entries = 256;
+ screen->root_visual->red_mask = 0xff;
+ screen->root_visual->green_mask = 0xff00;
+ screen->root_visual->blue_mask = 0xff0000;
+ }
+ }
+ screen->root_visual->bits_per_rgb = screen->root_depth;
+ ReleaseDC(NULL, dc);
+
+ /*
+ * Note that these pixel values are not palette relative.
+ */
+
+ screen->white_pixel = RGB(255, 255, 255);
+ screen->black_pixel = RGB(0, 0, 0);
+
+ display->screens = screen;
+ display->nscreens = 1;
+ display->default_screen = 0;
+ screen->cmap = XCreateColormap(display, None, screen->root_visual,
+ AllocNone);
+ winDisplay = (TkDisplay *) ckalloc(sizeof(TkDisplay));
+ winDisplay->display = display;
+ return winDisplay;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCloseDisplay --
+ *
+ * Closes and deallocates a Display structure created with the
+ * TkpOpenDisplay function.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees up memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpCloseDisplay(dispPtr)
+ TkDisplay *dispPtr;
+{
+ Display *display = dispPtr->display;
+ HWND hwnd;
+
+ if (dispPtr != winDisplay) {
+ panic("TkpCloseDisplay: tried to call TkpCloseDisplay on another display");
+ return;
+ }
+
+ /*
+ * Force the clipboard to be rendered if we are the clipboard owner.
+ */
+
+ if (dispPtr->clipWindow) {
+ hwnd = Tk_GetHWND(Tk_WindowId(dispPtr->clipWindow));
+ if (GetClipboardOwner() == hwnd) {
+ OpenClipboard(hwnd);
+ EmptyClipboard();
+ TkWinClipboardRender(dispPtr, CF_TEXT);
+ CloseClipboard();
+ }
+ }
+
+ winDisplay = NULL;
+
+ if (display->display_name != (char *) NULL) {
+ ckfree(display->display_name);
+ }
+ if (display->screens != (Screen *) NULL) {
+ if (display->screens->root_visual != NULL) {
+ ckfree((char *) display->screens->root_visual);
+ }
+ if (display->screens->root != None) {
+ ckfree((char *) display->screens->root);
+ }
+ if (display->screens->cmap != None) {
+ XFreeColormap(display, display->screens->cmap);
+ }
+ ckfree((char *) display->screens);
+ }
+ ckfree((char *) display);
+ ckfree((char *) dispPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * XBell --
+ *
+ * Generate a beep.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Plays a sounds out the system speakers.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+XBell(display, percent)
+ Display* display;
+ int percent;
+{
+ MessageBeep(MB_OK);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinChildProc --
+ *
+ * Callback from Windows whenever an event occurs on a child
+ * window.
+ *
+ * Results:
+ * Standard Windows return value.
+ *
+ * Side effects:
+ * May process events off the Tk event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+LRESULT CALLBACK
+TkWinChildProc(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ LRESULT result;
+
+ switch (message) {
+ case WM_SETCURSOR:
+ /*
+ * Short circuit the WM_SETCURSOR message since we set
+ * the cursor elsewhere.
+ */
+
+ result = TRUE;
+ break;
+
+ case WM_CREATE:
+ case WM_ERASEBKGND:
+ case WM_WINDOWPOSCHANGED:
+ result = 0;
+ break;
+
+ case WM_PAINT:
+ GenerateXEvent(hwnd, message, wParam, lParam);
+ result = DefWindowProc(hwnd, message, wParam, lParam);
+ break;
+
+ case TK_CLAIMFOCUS:
+ case TK_GEOMETRYREQ:
+ case TK_ATTACHWINDOW:
+ case TK_DETACHWINDOW:
+ result = TkWinEmbeddedEventProc(hwnd, message, wParam, lParam);
+ break;
+
+ default:
+ if (!Tk_TranslateWinEvent(hwnd, message, wParam, lParam,
+ &result)) {
+ result = DefWindowProc(hwnd, message, wParam, lParam);
+ }
+ break;
+ }
+
+ /*
+ * Handle any newly queued events before returning control to Windows.
+ */
+
+ Tcl_ServiceAll();
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_TranslateWinEvent --
+ *
+ * This function is called by widget window procedures to handle
+ * the translation from Win32 events to Tk events.
+ *
+ * Results:
+ * Returns 1 if the event was handled, else 0.
+ *
+ * Side effects:
+ * Depends on the event.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_TranslateWinEvent(hwnd, message, wParam, lParam, resultPtr)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+ LRESULT *resultPtr;
+{
+ *resultPtr = 0;
+ switch (message) {
+ case WM_RENDERFORMAT: {
+ TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd);
+ if (winPtr) {
+ TkWinClipboardRender(winPtr->dispPtr, wParam);
+ }
+ return 1;
+ }
+
+ case WM_COMMAND:
+ case WM_NOTIFY:
+ case WM_VSCROLL:
+ case WM_HSCROLL: {
+ /*
+ * Reflect these messages back to the sender so that they
+ * can be handled by the window proc for the control. Note
+ * that we need to be careful not to reflect a message that
+ * is targeted to this window, or we will loop.
+ */
+
+ HWND target = (message == WM_NOTIFY)
+ ? ((NMHDR*)lParam)->hwndFrom : (HWND) lParam;
+ if (target && target != hwnd) {
+ *resultPtr = SendMessage(target, message, wParam, lParam);
+ return 1;
+ }
+ break;
+ }
+
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONDBLCLK:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MOUSEMOVE:
+ Tk_PointerEvent(hwnd, (short) LOWORD(lParam),
+ (short) HIWORD(lParam));
+ return 1;
+
+ case WM_CLOSE:
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ case WM_DESTROYCLIPBOARD:
+ case WM_CHAR:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ GenerateXEvent(hwnd, message, wParam, lParam);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GenerateXEvent --
+ *
+ * This routine generates an X event from the corresponding
+ * Windows event.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Queues one or more X events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GenerateXEvent(hwnd, message, wParam, lParam)
+ HWND hwnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+{
+ XEvent event;
+ TkWindow *winPtr = (TkWindow *)Tk_HWNDToWindow(hwnd);
+
+ if (!winPtr || winPtr->window == None) {
+ return;
+ }
+
+ event.xany.serial = winPtr->display->request++;
+ event.xany.send_event = False;
+ event.xany.display = winPtr->display;
+ event.xany.window = winPtr->window;
+
+ switch (message) {
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+
+ event.type = Expose;
+ BeginPaint(hwnd, &ps);
+ event.xexpose.x = ps.rcPaint.left;
+ event.xexpose.y = ps.rcPaint.top;
+ event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left;
+ event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top;
+ EndPaint(hwnd, &ps);
+ event.xexpose.count = 0;
+ break;
+ }
+
+ case WM_CLOSE:
+ event.type = ClientMessage;
+ event.xclient.message_type =
+ Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS");
+ event.xclient.format = 32;
+ event.xclient.data.l[0] =
+ Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW");
+ break;
+
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS: {
+ TkWindow *otherWinPtr = (TkWindow *)Tk_HWNDToWindow((HWND) wParam);
+
+ /*
+ * Compare toplevel windows to avoid reporting focus
+ * changes within the same toplevel.
+ */
+
+ while (!(winPtr->flags & TK_TOP_LEVEL)) {
+ winPtr = winPtr->parentPtr;
+ if (winPtr == NULL) {
+ return;
+ }
+ }
+ while (otherWinPtr && !(otherWinPtr->flags & TK_TOP_LEVEL)) {
+ otherWinPtr = otherWinPtr->parentPtr;
+ }
+ if (otherWinPtr == winPtr) {
+ return;
+ }
+
+ event.xany.window = winPtr->window;
+ event.type = (message == WM_SETFOCUS) ? FocusIn : FocusOut;
+ event.xfocus.mode = NotifyNormal;
+ event.xfocus.detail = NotifyNonlinear;
+ break;
+ }
+
+ case WM_DESTROYCLIPBOARD:
+ event.type = SelectionClear;
+ event.xselectionclear.selection =
+ Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD");
+ event.xselectionclear.time = TkpGetMS();
+ break;
+
+ case WM_CHAR:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYDOWN:
+ case WM_KEYUP: {
+ unsigned int state = GetState(message, wParam, lParam);
+ Time time = TkpGetMS();
+ POINT clientPoint;
+ POINTS rootPoint; /* Note: POINT and POINTS are different */
+ DWORD msgPos;
+
+ /*
+ * Compute the screen and window coordinates of the event.
+ */
+
+ msgPos = GetMessagePos();
+ rootPoint = MAKEPOINTS(msgPos);
+ clientPoint.x = rootPoint.x;
+ clientPoint.y = rootPoint.y;
+ ScreenToClient(hwnd, &clientPoint);
+
+ /*
+ * Set up the common event fields.
+ */
+
+ event.xbutton.root = RootWindow(winPtr->display,
+ winPtr->screenNum);
+ event.xbutton.subwindow = None;
+ event.xbutton.x = clientPoint.x;
+ event.xbutton.y = clientPoint.y;
+ event.xbutton.x_root = rootPoint.x;
+ event.xbutton.y_root = rootPoint.y;
+ event.xbutton.state = state;
+ event.xbutton.time = time;
+ event.xbutton.same_screen = True;
+
+ /*
+ * Now set up event specific fields.
+ */
+
+ switch (message) {
+ case WM_SYSKEYDOWN:
+ case WM_KEYDOWN:
+ /*
+ * Check for translated characters in the event queue.
+ * Setting xany.send_event to -1 indicates to the
+ * Windows implementation of XLookupString that this
+ * event was generated by windows and that the Windows
+ * extension xkey.trans_chars is filled with the
+ * characters that came from the TranslateMessage
+ * call. If it is not -1, xkey.keycode is the
+ * virtual key being sent programmatically by generic
+ * code.
+ */
+
+ event.type = KeyPress;
+ event.xany.send_event = -1;
+ event.xkey.keycode = wParam;
+ GetTranslatedKey(&event.xkey);
+ break;
+
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ /*
+ * We don't check for translated characters on keyup
+ * because Tk won't know what to do with them. Instead, we
+ * wait for the WM_CHAR messages which will follow.
+ */
+ event.type = KeyRelease;
+ event.xkey.keycode = wParam;
+ event.xkey.nchars = 0;
+ break;
+
+ case WM_CHAR:
+ /*
+ * Synthesize both a KeyPress and a KeyRelease.
+ */
+
+ event.type = KeyPress;
+ event.xany.send_event = -1;
+ event.xkey.keycode = 0;
+ event.xkey.nchars = 1;
+ event.xkey.trans_chars[0] = (char) wParam;
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+ event.type = KeyRelease;
+ break;
+ }
+ break;
+ }
+
+ default:
+ return;
+ }
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetState --
+ *
+ * This function constructs a state mask for the mouse buttons
+ * and modifier keys as they were before the event occured.
+ *
+ * Results:
+ * Returns a composite value of all the modifier and button state
+ * flags that were set at the time the event occurred.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static unsigned int
+GetState(message, wParam, lParam)
+ UINT message; /* Win32 message type */
+ WPARAM wParam; /* wParam of message, used if key message */
+ LPARAM lParam; /* lParam of message, used if key message */
+{
+ int mask;
+ int prevState; /* 1 if key was previously down */
+ unsigned int state = TkWinGetModifierState();
+
+ /*
+ * If the event is a key press or release, we check for modifier
+ * keys so we can report the state of the world before the event.
+ */
+
+ if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN
+ || message == WM_SYSKEYUP || message == WM_KEYUP) {
+ mask = 0;
+ prevState = HIWORD(lParam) & KF_REPEAT;
+ switch(wParam) {
+ case VK_SHIFT:
+ mask = ShiftMask;
+ break;
+ case VK_CONTROL:
+ mask = ControlMask;
+ break;
+ case VK_MENU:
+ mask = Mod2Mask;
+ break;
+ case VK_CAPITAL:
+ if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
+ mask = LockMask;
+ prevState = ((state & mask) ^ prevState) ? 0 : 1;
+ }
+ break;
+ case VK_NUMLOCK:
+ if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
+ mask = Mod1Mask;
+ prevState = ((state & mask) ^ prevState) ? 0 : 1;
+ }
+ break;
+ case VK_SCROLL:
+ if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
+ mask = Mod3Mask;
+ prevState = ((state & mask) ^ prevState) ? 0 : 1;
+ }
+ break;
+ }
+ if (prevState) {
+ state |= mask;
+ } else {
+ state &= ~mask;
+ }
+ }
+ return state;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTranslatedKey --
+ *
+ * Retrieves WM_CHAR messages that are placed on the system queue
+ * by the TranslateMessage system call and places them in the
+ * given KeyPress event.
+ *
+ * Results:
+ * Sets the trans_chars and nchars member of the key event.
+ *
+ * Side effects:
+ * Removes any WM_CHAR messages waiting on the top of the system
+ * event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetTranslatedKey(xkey)
+ XKeyEvent *xkey;
+{
+ MSG msg;
+
+ xkey->nchars = 0;
+
+ while (xkey->nchars < XMaxTransChars
+ && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+ if (msg.message == WM_CHAR) {
+ xkey->trans_chars[xkey->nchars] = (char) msg.wParam;
+ xkey->nchars++;
+ GetMessage(&msg, NULL, 0, 0);
+ if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) {
+ xkey->state = 0;
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_FreeXId --
+ *
+ * This inteface is not needed under Windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_FreeXId(display, xid)
+ Display *display;
+ XID xid;
+{
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWinResendEvent --
+ *
+ * This function converts an X event into a Windows event and
+ * invokes the specified windo procedure.
+ *
+ * Results:
+ * A standard Windows result.
+ *
+ * Side effects:
+ * Invokes the window procedure
+ *
+ *----------------------------------------------------------------------
+ */
+
+LRESULT
+TkWinResendEvent(wndproc, hwnd, eventPtr)
+ WNDPROC wndproc;
+ HWND hwnd;
+ XEvent *eventPtr;
+{
+ UINT msg;
+ WPARAM wparam;
+ LPARAM lparam;
+
+ if (eventPtr->type == ButtonPress) {
+ switch (eventPtr->xbutton.button) {
+ case Button1:
+ msg = WM_LBUTTONDOWN;
+ wparam = MK_LBUTTON;
+ break;
+ case Button2:
+ msg = WM_MBUTTONDOWN;
+ wparam = MK_MBUTTON;
+ break;
+ case Button3:
+ msg = WM_RBUTTONDOWN;
+ wparam = MK_RBUTTON;
+ break;
+ default:
+ return 0;
+ }
+ if (eventPtr->xbutton.state & Button1Mask) {
+ wparam |= MK_LBUTTON;
+ }
+ if (eventPtr->xbutton.state & Button2Mask) {
+ wparam |= MK_MBUTTON;
+ }
+ if (eventPtr->xbutton.state & Button3Mask) {
+ wparam |= MK_RBUTTON;
+ }
+ if (eventPtr->xbutton.state & ShiftMask) {
+ wparam |= MK_SHIFT;
+ }
+ if (eventPtr->xbutton.state & ControlMask) {
+ wparam |= MK_CONTROL;
+ }
+ lparam = MAKELPARAM((short) eventPtr->xbutton.x,
+ (short) eventPtr->xbutton.y);
+ } else {
+ return 0;
+ }
+ return CallWindowProc(wndproc, hwnd, msg, wparam, lparam);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetMS --
+ *
+ * Return a relative time in milliseconds. It doesn't matter
+ * when the epoch was.
+ *
+ * Results:
+ * Number of milliseconds.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+unsigned long
+TkpGetMS()
+{
+ return GetCurrentTime();
+}
diff --git a/win/winMain.c b/win/winMain.c
new file mode 100644
index 0000000..f263339
--- /dev/null
+++ b/win/winMain.c
@@ -0,0 +1,323 @@
+/*
+ * winMain.c --
+ *
+ * Main entry point for wish and other Tk-based applications.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) winMain.c 1.33 96/12/17 12:56:14
+ */
+
+#include <tk.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <malloc.h>
+#include <locale.h>
+
+/*
+ * The following declarations refer to internal Tk routines. These
+ * interfaces are available for use, but are not supported.
+ */
+
+EXTERN void TkConsoleCreate(void);
+EXTERN int TkConsoleInit(Tcl_Interp *interp);
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr));
+static void WishPanic _ANSI_ARGS_(TCL_VARARGS(char *,format));
+
+#ifdef TK_TEST
+EXTERN int Tktest_Init(Tcl_Interp *interp);
+#endif /* TK_TEST */
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WinMain --
+ *
+ * Main entry point from Windows.
+ *
+ * Results:
+ * Returns false if initialization fails, otherwise it never
+ * returns.
+ *
+ * Side effects:
+ * Just about anything, since from here we call arbitrary Tcl code.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int APIENTRY
+WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
+ HINSTANCE hInstance;
+ HINSTANCE hPrevInstance;
+ LPSTR lpszCmdLine;
+ int nCmdShow;
+{
+ char **argv, *p;
+ int argc;
+ char buffer[MAX_PATH];
+
+ Tcl_SetPanicProc(WishPanic);
+
+ /*
+ * Set up the default locale to be standard "C" locale so parsing
+ * is performed correctly.
+ */
+
+ setlocale(LC_ALL, "C");
+
+
+ /*
+ * Increase the application queue size from default value of 8.
+ * At the default value, cross application SendMessage of WM_KILLFOCUS
+ * will fail because the handler will not be able to do a PostMessage!
+ * This is only needed for Windows 3.x, since NT dynamically expands
+ * the queue.
+ */
+ SetMessageQueue(64);
+
+ /*
+ * Create the console channels and install them as the standard
+ * channels. All I/O will be discarded until TkConsoleInit is
+ * called to attach the console to a text widget.
+ */
+
+ TkConsoleCreate();
+
+ setargv(&argc, &argv);
+
+ /*
+ * Replace argv[0] with full pathname of executable, and forward
+ * slashes substituted for backslashes.
+ */
+
+ GetModuleFileName(NULL, buffer, sizeof(buffer));
+ argv[0] = buffer;
+ for (p = buffer; *p != '\0'; p++) {
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+
+ Tk_Main(argc, argv, Tcl_AppInit);
+ return 1;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ if (Tk_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit);
+
+ /*
+ * Initialize the console only if we are running as an interactive
+ * application.
+ */
+
+ if (TkConsoleInit(interp) == TCL_ERROR) {
+ goto error;
+ }
+
+#ifdef TK_TEST
+ if (Tktest_Init(interp) == TCL_ERROR) {
+ goto error;
+ }
+ Tcl_StaticPackage(interp, "Tktest", Tktest_Init,
+ (Tcl_PackageInitProc *) NULL);
+#endif /* TK_TEST */
+
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/wishrc.tcl", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+
+error:
+ WishPanic(interp->result);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WishPanic --
+ *
+ * Display a message and exit.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Exits the program.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+WishPanic TCL_VARARGS_DEF(char *,arg1)
+{
+ va_list argList;
+ char buf[1024];
+ char *format;
+
+ format = TCL_VARARGS_START(char *,arg1,argList);
+ vsprintf(buf, format, argList);
+
+ MessageBeep(MB_ICONEXCLAMATION);
+ MessageBox(NULL, buf, "Fatal Error in Wish",
+ MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND);
+#ifdef _MSC_VER
+ _asm {
+ int 3
+ }
+#endif
+ ExitProcess(1);
+}
+/*
+ *-------------------------------------------------------------------------
+ *
+ * setargv --
+ *
+ * Parse the Windows command line string into argc/argv. Done here
+ * because we don't trust the builtin argument parser in crt0.
+ * Windows applications are responsible for breaking their command
+ * line into arguments.
+ *
+ * 2N backslashes + quote -> N backslashes + begin quoted string
+ * 2N + 1 backslashes + quote -> literal
+ * N backslashes + non-quote -> literal
+ * quote + quote in a quoted string -> single quote
+ * quote + quote not in quoted string -> empty string
+ * quote -> begin quoted string
+ *
+ * Results:
+ * Fills argcPtr with the number of arguments and argvPtr with the
+ * array of arguments.
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *--------------------------------------------------------------------------
+ */
+
+static void
+setargv(argcPtr, argvPtr)
+ int *argcPtr; /* Filled with number of argument strings. */
+ char ***argvPtr; /* Filled with argument strings (malloc'd). */
+{
+ char *cmdLine, *p, *arg, *argSpace;
+ char **argv;
+ int argc, size, inquote, copy, slashes;
+
+ cmdLine = GetCommandLine();
+
+ /*
+ * Precompute an overly pessimistic guess at the number of arguments
+ * in the command line by counting non-space spans.
+ */
+
+ size = 2;
+ for (p = cmdLine; *p != '\0'; p++) {
+ if (isspace(*p)) {
+ size++;
+ while (isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+ }
+ }
+ argSpace = (char *) ckalloc((unsigned) (size * sizeof(char *)
+ + strlen(cmdLine) + 1));
+ argv = (char **) argSpace;
+ argSpace += size * sizeof(char *);
+ size--;
+
+ p = cmdLine;
+ for (argc = 0; argc < size; argc++) {
+ argv[argc] = arg = argSpace;
+ while (isspace(*p)) {
+ p++;
+ }
+ if (*p == '\0') {
+ break;
+ }
+
+ inquote = 0;
+ slashes = 0;
+ while (1) {
+ copy = 1;
+ while (*p == '\\') {
+ slashes++;
+ p++;
+ }
+ if (*p == '"') {
+ if ((slashes & 1) == 0) {
+ copy = 0;
+ if ((inquote) && (p[1] == '"')) {
+ p++;
+ copy = 1;
+ } else {
+ inquote = !inquote;
+ }
+ }
+ slashes >>= 1;
+ }
+
+ while (slashes) {
+ *arg = '\\';
+ arg++;
+ slashes--;
+ }
+
+ if ((*p == '\0') || (!inquote && isspace(*p))) {
+ break;
+ }
+ if (copy != 0) {
+ *arg = *p;
+ arg++;
+ }
+ p++;
+ }
+ *arg = '\0';
+ argSpace = arg + 1;
+ }
+ argv[argc] = NULL;
+
+ *argcPtr = argc;
+ *argvPtr = argv;
+}
+