diff options
Diffstat (limited to 'win')
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; +} + |